summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
Diffstat (limited to 'storage')
-rw-r--r--storage/archive/azio.c14
-rw-r--r--storage/archive/azlib.h2
-rw-r--r--storage/archive/ha_archive.cc41
-rw-r--r--storage/csv/ha_tina.cc37
-rw-r--r--storage/example/ha_example.cc35
-rw-r--r--storage/example/plug.in2
-rw-r--r--storage/federated/ha_federated.cc43
-rw-r--r--storage/federated/ha_federated.h1
-rw-r--r--storage/heap/ha_heap.cc2
-rw-r--r--storage/heap/hp_delete.c3
-rw-r--r--storage/heap/hp_rfirst.c11
-rw-r--r--storage/heap/hp_rnext.c29
-rw-r--r--storage/innobase/buf/buf0lru.c2
-rw-r--r--storage/innobase/dict/dict0dict.c14
-rw-r--r--storage/innobase/dict/dict0load.c80
-rw-r--r--storage/innobase/handler/ha_innodb.cc327
-rw-r--r--storage/innobase/handler/ha_innodb.h14
-rw-r--r--storage/innobase/ibuf/ibuf0ibuf.c4
-rw-r--r--storage/innobase/include/db0err.h6
-rw-r--r--storage/innobase/include/ha_prototypes.h16
-rw-r--r--storage/innobase/include/mach0data.h11
-rw-r--r--storage/innobase/include/mach0data.ic37
-rw-r--r--storage/innobase/include/mem0dbg.h16
-rw-r--r--storage/innobase/include/mem0mem.ic16
-rw-r--r--storage/innobase/include/page0cur.h1
-rw-r--r--storage/innobase/include/rem0rec.ic1
-rw-r--r--storage/innobase/include/row0mysql.h13
-rw-r--r--storage/innobase/include/sync0rw.h2
-rw-r--r--storage/innobase/include/univ.i28
-rw-r--r--storage/innobase/include/ut0ut.h18
-rw-r--r--storage/innobase/mem/mem0dbg.c40
-rw-r--r--storage/innobase/mem/mem0mem.c6
-rw-r--r--storage/innobase/row/row0mysql.c82
-rw-r--r--storage/innobase/row/row0sel.c78
-rw-r--r--storage/innobase/sync/sync0rw.c6
-rw-r--r--storage/innobase/sync/sync0sync.c2
-rw-r--r--storage/innobase/ut/ut0ut.c71
-rw-r--r--storage/myisam/ft_boolean_search.c4
-rw-r--r--storage/myisam/ft_nlq_search.c3
-rw-r--r--storage/myisam/ha_myisam.cc16
-rw-r--r--storage/myisam/ha_myisam.h4
-rw-r--r--storage/myisam/mi_check.c265
-rw-r--r--storage/myisam/mi_dynrec.c68
-rw-r--r--storage/myisam/mi_open.c9
-rw-r--r--storage/myisam/mi_packrec.c3
-rw-r--r--storage/myisam/mi_write.c2
-rw-r--r--storage/myisam/myisamchk.c7
-rw-r--r--storage/myisam/myisampack.c2
-rw-r--r--storage/myisam/rt_index.c18
-rw-r--r--storage/myisam/rt_mbr.c5
-rw-r--r--storage/myisam/sort.c22
-rw-r--r--storage/myisammrg/ha_myisammrg.cc701
-rw-r--r--storage/myisammrg/ha_myisammrg.h9
-rw-r--r--storage/myisammrg/myrg_close.c35
-rw-r--r--storage/myisammrg/myrg_extra.c4
-rw-r--r--storage/myisammrg/myrg_open.c349
-rw-r--r--storage/ndb/include/kernel/AttributeHeader.hpp10
-rw-r--r--storage/ndb/include/kernel/GlobalSignalNumbers.h8
-rw-r--r--storage/ndb/include/kernel/signaldata/AccScan.hpp1
-rw-r--r--storage/ndb/include/kernel/signaldata/CopyFrag.hpp41
-rw-r--r--storage/ndb/include/kernel/signaldata/ScanTab.hpp1
-rw-r--r--storage/ndb/include/kernel/signaldata/TcKeyConf.hpp2
-rw-r--r--storage/ndb/include/kernel/signaldata/TcKeyRef.hpp3
-rw-r--r--storage/ndb/include/kernel/signaldata/TcRollbackRep.hpp3
-rw-r--r--storage/ndb/include/ndb_version.h.in47
-rw-r--r--storage/ndb/include/ndbapi/Ndb.hpp3
-rw-r--r--storage/ndb/include/ndbapi/NdbDictionary.hpp2
-rw-r--r--storage/ndb/include/ndbapi/NdbOperation.hpp5
-rw-r--r--storage/ndb/include/ndbapi/NdbPool.hpp3
-rw-r--r--storage/ndb/include/ndbapi/NdbScanFilter.hpp29
-rw-r--r--storage/ndb/include/ndbapi/NdbTransaction.hpp9
-rw-r--r--storage/ndb/include/ndbapi/ndbapi_limits.h2
-rw-r--r--storage/ndb/include/portlib/NdbThread.h2
-rw-r--r--storage/ndb/include/util/ndb_rand.h33
-rw-r--r--storage/ndb/src/common/debugger/EventLogger.cpp12
-rw-r--r--storage/ndb/src/common/debugger/signaldata/SignalNames.cpp4
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp4
-rw-r--r--storage/ndb/src/common/transporter/Transporter.cpp4
-rw-r--r--storage/ndb/src/common/transporter/TransporterRegistry.cpp3
-rw-r--r--storage/ndb/src/common/util/Makefile.am3
-rw-r--r--storage/ndb/src/common/util/ndb_rand.c40
-rw-r--r--storage/ndb/src/cw/cpcd/APIService.cpp2
-rw-r--r--storage/ndb/src/kernel/blocks/ERROR_codes.txt20
-rw-r--r--storage/ndb/src/kernel/blocks/backup/Backup.cpp5
-rw-r--r--storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp89
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp12
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp2
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp18
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp5
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp267
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp21
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp7
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp399
-rw-r--r--storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp14
-rw-r--r--storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp245
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp14
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp264
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp6
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp66
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp10
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp88
-rw-r--r--storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp5
-rw-r--r--storage/ndb/src/kernel/blocks/lgman.cpp12
-rw-r--r--storage/ndb/src/kernel/blocks/pgman.cpp62
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp3
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp26
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp362
-rw-r--r--storage/ndb/src/kernel/blocks/suma/Suma.cpp60
-rw-r--r--storage/ndb/src/kernel/blocks/tsman.cpp62
-rw-r--r--storage/ndb/src/kernel/blocks/tsman.hpp24
-rw-r--r--storage/ndb/src/kernel/vm/SimulatedBlock.cpp2
-rw-r--r--storage/ndb/src/mgmclient/CommandInterpreter.cpp29
-rw-r--r--storage/ndb/src/mgmsrv/MgmtSrvr.cpp87
-rw-r--r--storage/ndb/src/mgmsrv/Services.cpp2
-rw-r--r--storage/ndb/src/ndbapi/ClusterMgr.cpp5
-rw-r--r--storage/ndb/src/ndbapi/NdbBlob.cpp2
-rw-r--r--storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp12
-rw-r--r--storage/ndb/src/ndbapi/NdbIndexOperation.cpp3
-rw-r--r--storage/ndb/src/ndbapi/NdbOperation.cpp6
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationDefine.cpp76
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationExec.cpp24
-rw-r--r--storage/ndb/src/ndbapi/NdbReceiver.cpp2
-rw-r--r--storage/ndb/src/ndbapi/NdbScanFilter.cpp244
-rw-r--r--storage/ndb/src/ndbapi/NdbScanOperation.cpp47
-rw-r--r--storage/ndb/src/ndbapi/NdbTransaction.cpp18
-rw-r--r--storage/ndb/src/ndbapi/TransporterFacade.cpp2
-rw-r--r--storage/ndb/src/ndbapi/ndberror.c5
-rwxr-xr-xstorage/ndb/test/include/DbUtil.hpp131
-rw-r--r--storage/ndb/test/include/HugoTransactions.hpp18
-rw-r--r--storage/ndb/test/include/UtilTransactions.hpp5
-rw-r--r--storage/ndb/test/ndbapi/acrt/NdbRepStress.cpp478
-rw-r--r--storage/ndb/test/ndbapi/testBasic.cpp35
-rw-r--r--storage/ndb/test/ndbapi/testDict.cpp263
-rw-r--r--storage/ndb/test/ndbapi/testIndex.cpp4
-rw-r--r--storage/ndb/test/ndbapi/testNodeRestart.cpp127
-rw-r--r--storage/ndb/test/ndbapi/testScan.cpp2
-rw-r--r--storage/ndb/test/ndbapi/testSystemRestart.cpp370
-rw-r--r--storage/ndb/test/ndbapi/test_event.cpp90
-rw-r--r--storage/ndb/test/run-test/daily-basic-tests.txt103
-rwxr-xr-xstorage/ndb/test/src/DbUtil.cpp337
-rw-r--r--storage/ndb/test/src/HugoOperations.cpp1
-rw-r--r--storage/ndb/test/src/HugoTransactions.cpp229
-rw-r--r--storage/ndb/test/src/Makefile.am2
-rw-r--r--storage/ndb/test/src/NDBT_Thread.cpp2
-rw-r--r--storage/ndb/test/src/NdbRestarts.cpp1
-rw-r--r--storage/ndb/test/src/UtilTransactions.cpp74
-rw-r--r--storage/ndb/test/tools/Makefile.am3
-rw-r--r--storage/ndb/test/tools/connect.cpp152
-rw-r--r--storage/ndb/tools/restore/Restore.cpp156
-rw-r--r--storage/ndb/tools/restore/Restore.hpp4
150 files changed, 6453 insertions, 1781 deletions
diff --git a/storage/archive/azio.c b/storage/archive/azio.c
index f6b1a6e733f..cada6c57918 100644
--- a/storage/archive/azio.c
+++ b/storage/archive/azio.c
@@ -262,7 +262,7 @@ void check_header(azio_stream *s)
if (len) s->inbuf[0] = s->stream.next_in[0];
errno = 0;
len = (uInt)my_read(s->file, (uchar *)s->inbuf + len, AZ_BUFSIZE_READ >> len, MYF(0));
- if (len == 0) s->z_err = Z_ERRNO;
+ if (len == (uInt)-1) s->z_err = Z_ERRNO;
s->stream.avail_in += len;
s->stream.next_in = s->inbuf;
if (s->stream.avail_in < 2) {
@@ -510,7 +510,7 @@ unsigned int ZEXPORT azread ( azio_stream *s, voidp buf, unsigned int len, int *
Writes the given number of uncompressed bytes into the compressed file.
azwrite returns the number of bytes actually written (0 in case of error).
*/
-unsigned int azwrite (azio_stream *s, voidpc buf, unsigned int len)
+unsigned int azwrite (azio_stream *s, const voidp buf, unsigned int len)
{
s->stream.next_in = (Bytef*)buf;
s->stream.avail_in = len;
@@ -681,8 +681,8 @@ my_off_t azseek (s, offset, whence)
/* There was a zmemzero here if inbuf was null -Brian */
while (offset > 0)
{
- uInt size = AZ_BUFSIZE_WRITE;
- if (offset < AZ_BUFSIZE_WRITE) size = (uInt)offset;
+ uInt size = AZ_BUFSIZE_READ;
+ if (offset < AZ_BUFSIZE_READ) size = (uInt)offset;
size = azwrite(s, s->inbuf, size);
if (size == 0) return -1L;
@@ -725,11 +725,11 @@ my_off_t azseek (s, offset, whence)
}
while (offset > 0) {
int error;
- unsigned int size = AZ_BUFSIZE_READ;
- if (offset < AZ_BUFSIZE_READ) size = (int)offset;
+ unsigned int size = AZ_BUFSIZE_WRITE;
+ if (offset < AZ_BUFSIZE_WRITE) size = (int)offset;
size = azread(s, s->outbuf, size, &error);
- if (error <= 0) return -1L;
+ if (error < 0) return -1L;
offset -= size;
}
return s->out;
diff --git a/storage/archive/azlib.h b/storage/archive/azlib.h
index a5bee1befae..47772b1c4fe 100644
--- a/storage/archive/azlib.h
+++ b/storage/archive/azlib.h
@@ -273,7 +273,7 @@ extern unsigned int azread ( azio_stream *s, voidp buf, unsigned int len, int *e
gzread returns the number of uncompressed bytes actually read (0 for
end of file, -1 for error). */
-extern unsigned int azwrite (azio_stream *s, voidpc buf, unsigned int len);
+extern unsigned int azwrite (azio_stream *s, const voidp buf, unsigned int len);
/*
Writes the given number of uncompressed bytes into the compressed file.
azwrite returns the number of uncompressed bytes actually written
diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc
index 6696eac2fbb..967e315d4a4 100644
--- a/storage/archive/ha_archive.cc
+++ b/storage/archive/ha_archive.cc
@@ -357,7 +357,7 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, int *rc)
{
DBUG_RETURN(NULL);
}
- stats.auto_increment_value= archive_tmp.auto_increment;
+ stats.auto_increment_value= archive_tmp.auto_increment + 1;
share->rows_recorded= (ha_rows)archive_tmp.rows;
share->crashed= archive_tmp.dirty;
azclose(&archive_tmp);
@@ -586,9 +586,7 @@ int ha_archive::create(const char *name, TABLE *table_arg,
DBUG_ENTER("ha_archive::create");
- stats.auto_increment_value= (create_info->auto_increment_value ?
- create_info->auto_increment_value -1 :
- (ulonglong) 0);
+ stats.auto_increment_value= create_info->auto_increment_value;
for (uint key= 0; key < table_arg->s->keys; key++)
{
@@ -673,7 +671,8 @@ int ha_archive::create(const char *name, TABLE *table_arg,
Yes you need to do this, because the starting value
for the autoincrement may not be zero.
*/
- create_stream.auto_increment= stats.auto_increment_value;
+ create_stream.auto_increment= stats.auto_increment_value ?
+ stats.auto_increment_value - 1 : 0;
if (azclose(&create_stream))
{
error= errno;
@@ -871,8 +870,8 @@ int ha_archive::write_row(uchar *buf)
else
{
if (temp_auto > share->archive_write.auto_increment)
- stats.auto_increment_value= share->archive_write.auto_increment=
- temp_auto;
+ stats.auto_increment_value=
+ (share->archive_write.auto_increment= temp_auto) + 1;
}
}
@@ -896,7 +895,7 @@ void ha_archive::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{
- *nb_reserved_values= 1;
+ *nb_reserved_values= ULONGLONG_MAX;
*first_value= share->archive_write.auto_increment + 1;
}
@@ -1241,8 +1240,8 @@ int ha_archive::rnd_pos(uchar * buf, uchar *pos)
DBUG_ENTER("ha_archive::rnd_pos");
ha_statistic_increment(&SSV::ha_read_rnd_next_count);
current_position= (my_off_t)my_get_ptr(pos, ref_length);
- (void)azseek(&archive, current_position, SEEK_SET);
-
+ if (azseek(&archive, current_position, SEEK_SET) == (my_off_t)(-1L))
+ DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
DBUG_RETURN(get_row(&archive, buf));
}
@@ -1315,7 +1314,8 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
if (!rc)
{
share->rows_recorded= 0;
- stats.auto_increment_value= share->archive_write.auto_increment= 0;
+ stats.auto_increment_value= 1;
+ share->archive_write.auto_increment= 0;
my_bitmap_map *org_bitmap= dbug_tmp_use_all_columns(table, table->read_set);
while (!(rc= get_row(&archive, table->record[0])))
@@ -1332,8 +1332,8 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
(ulonglong) field->val_int(table->record[0] +
field->offset(table->record[0]));
if (share->archive_write.auto_increment < auto_value)
- stats.auto_increment_value= share->archive_write.auto_increment=
- auto_value;
+ stats.auto_increment_value=
+ (share->archive_write.auto_increment= auto_value) + 1;
}
}
@@ -1418,18 +1418,9 @@ void ha_archive::update_create_info(HA_CREATE_INFO *create_info)
DBUG_ENTER("ha_archive::update_create_info");
ha_archive::info(HA_STATUS_AUTO);
- if (create_info->used_fields & HA_CREATE_USED_AUTO)
+ if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
{
- /*
- Internally Archive keeps track of last used, not next used.
- To make the output look like MyISAM we add 1 here.
-
- This is not completely compatible with MYISAM though, since
- MyISAM will record on "SHOW CREATE TABLE" the last position,
- where we will report the original position the table was
- created with.
- */
- create_info->auto_increment_value= stats.auto_increment_value + 1;
+ create_info->auto_increment_value= stats.auto_increment_value;
}
if (!(my_readlink(share->real_path, share->data_file_name, MYF(0))))
@@ -1494,7 +1485,7 @@ int ha_archive::info(uint flag)
pthread_mutex_lock(&share->mutex);
azflush(&archive, Z_SYNC_FLUSH);
pthread_mutex_unlock(&share->mutex);
- stats.auto_increment_value= archive.auto_increment;
+ stats.auto_increment_value= archive.auto_increment + 1;
}
DBUG_RETURN(0);
diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc
index 9a7781e017d..c9fab79a4c5 100644
--- a/storage/csv/ha_tina.cc
+++ b/storage/csv/ha_tina.cc
@@ -471,22 +471,22 @@ int ha_tina::encode_quote(uchar *buf)
{
const char *ptr;
const char *end_ptr;
-
+ const bool was_null= (*field)->is_null();
+
/*
- CSV does not support nulls. Write quoted 0 to the buffer. In fact,
- (*field)->val_str(&attribute,&attribute) would usually return 0
- in this case but we write it explicitly here.
- Basically this is a safety check, as no one ensures that the
- field content is cleaned up every time we use Field::set_null()
- in the code.
+ assistance for backwards compatibility in production builds.
+ note: this will not work for ENUM columns.
*/
- if ((*field)->is_null())
+ if (was_null)
{
- buffer.append(STRING_WITH_LEN("\"0\","));
- continue;
+ (*field)->set_default();
+ (*field)->set_notnull();
}
(*field)->val_str(&attribute,&attribute);
+
+ if (was_null)
+ (*field)->set_null();
if ((*field)->str_needs_quotes())
{
@@ -1195,8 +1195,8 @@ int ha_tina::rnd_end()
The sort is needed when there were updates/deletes with random orders.
It sorts so that we move the firts blocks to the beginning.
*/
- qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set),
- (qsort_cmp)sort_set);
+ my_qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set),
+ (qsort_cmp)sort_set);
off_t write_begin= 0, write_end;
@@ -1480,6 +1480,19 @@ int ha_tina::create(const char *name, TABLE *table_arg,
File create_file;
DBUG_ENTER("ha_tina::create");
+ /*
+ check columns
+ */
+ for (Field **field= table_arg->s->field; *field; field++)
+ {
+ if ((*field)->real_maybe_null())
+ {
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ }
+ }
+
+
if ((create_file= my_create(fn_format(name_buff, name, "", CSM_EXT,
MY_REPLACE_EXT|MY_UNPACK_FILENAME), 0,
O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc
index 65e28da1fe3..6d9f4841e06 100644
--- a/storage/example/ha_example.cc
+++ b/storage/example/ha_example.cc
@@ -79,6 +79,10 @@
ha_example::open() would also have been necessary. Calls to
ha_example::extra() are hints as to what will be occuring to the request.
+ A Longer Example can be found called the "Skeleton Engine" which can be
+ found on TangentOrg. It has both an engine and a full build environment
+ for building a pluggable storage engine.
+
Happy coding!<br>
-Brian
*/
@@ -132,7 +136,6 @@ static int example_init_func(void *p)
(hash_get_key) example_get_key,0,0);
example_hton->state= SHOW_OPTION_YES;
- example_hton->db_type= DB_TYPE_EXAMPLE_DB;
example_hton->create= example_create_handler;
example_hton->flags= HTON_CAN_RECREATE;
@@ -845,6 +848,34 @@ int ha_example::create(const char *name, TABLE *table_arg,
struct st_mysql_storage_engine example_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
+static ulong srv_enum_var= 0;
+
+const char *enum_var_names[]=
+{
+ "e1", "e2", NullS
+};
+
+TYPELIB enum_var_typelib=
+{
+ array_elements(enum_var_names) - 1, "enum_var_typelib",
+ enum_var_names, NULL
+};
+
+static MYSQL_SYSVAR_ENUM(
+ enum_var, // name
+ srv_enum_var, // varname
+ PLUGIN_VAR_RQCMDARG, // opt
+ "Sample ENUM system variable.", // comment
+ NULL, // check
+ NULL, // update
+ 0, // def
+ &enum_var_typelib); // typelib
+
+static struct st_mysql_sys_var* example_system_variables[]= {
+ MYSQL_SYSVAR(enum_var),
+ NULL
+};
+
mysql_declare_plugin(example)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
@@ -857,7 +888,7 @@ mysql_declare_plugin(example)
example_done_func, /* Plugin Deinit */
0x0001 /* 0.1 */,
NULL, /* status variables */
- NULL, /* system variables */
+ example_system_variables, /* system variables */
NULL /* config options */
}
mysql_declare_plugin_end;
diff --git a/storage/example/plug.in b/storage/example/plug.in
index ba35b1ea117..ee6beaac64f 100644
--- a/storage/example/plug.in
+++ b/storage/example/plug.in
@@ -1,3 +1,3 @@
MYSQL_STORAGE_ENGINE(example,, [Example Storage Engine],
- [Skeleton for Storage Engines for developers], [max,max-no-ndb])
+ [Example for Storage Engines for developers], [max,max-no-ndb])
MYSQL_PLUGIN_DYNAMIC(example, [ha_example.la])
diff --git a/storage/federated/ha_federated.cc b/storage/federated/ha_federated.cc
index ded0ce88484..091a26f6f36 100644
--- a/storage/federated/ha_federated.cc
+++ b/storage/federated/ha_federated.cc
@@ -2408,6 +2408,24 @@ error:
}
+/*
+ This method is used exlusevely by filesort() to check if we
+ can create sorting buffers of necessary size.
+ If the handler returns more records that it declares
+ here server can just crash on filesort().
+ We cannot guarantee that's not going to happen with
+ the FEDERATED engine, as we have records==0 always if the
+ client is a VIEW, and for the table the number of
+ records can inpredictably change during execution.
+ So we return maximum possible value here.
+*/
+
+ha_rows ha_federated::estimate_rows_upper_bound()
+{
+ return HA_POS_ERROR;
+}
+
+
/* Initialized at each key walk (called multiple times unlike rnd_init()) */
int ha_federated::index_init(uint keynr, bool sorted)
@@ -2769,7 +2787,12 @@ int ha_federated::info(uint flag)
status_query_string.length(0);
result= mysql_store_result(mysql);
- if (!result)
+
+ /*
+ We're going to use fields num. 4, 12 and 13 of the resultset,
+ so make sure we have these fields.
+ */
+ if (!result || (mysql_num_fields(result) < 14))
goto error;
if (!mysql_num_rows(result))
@@ -2795,15 +2818,15 @@ int ha_federated::info(uint flag)
stats.records= (ha_rows) my_strtoll10(row[4], (char**) 0,
&error);
if (row[5] != NULL)
- stats.mean_rec_length= (ha_rows) my_strtoll10(row[5], (char**) 0, &error);
+ stats.mean_rec_length= (ulong) my_strtoll10(row[5], (char**) 0, &error);
stats.data_file_length= stats.records * stats.mean_rec_length;
if (row[12] != NULL)
- stats.update_time= (ha_rows) my_strtoll10(row[12], (char**) 0,
+ stats.update_time= (time_t) my_strtoll10(row[12], (char**) 0,
&error);
if (row[13] != NULL)
- stats.check_time= (ha_rows) my_strtoll10(row[13], (char**) 0,
+ stats.check_time= (time_t) my_strtoll10(row[13], (char**) 0,
&error);
}
/*
@@ -3169,7 +3192,7 @@ int ha_federated::external_lock(THD *thd, int lock_type)
#ifdef XXX_SUPERCEDED_BY_WL2952
if (lock_type != F_UNLCK)
{
- ha_federated *trx= (ha_federated *)thd->ha_data[ht->slot];
+ ha_federated *trx= (ha_federated *)thd_get_ha_data(thd, ht);
DBUG_PRINT("info",("federated not lock F_UNLCK"));
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
@@ -3200,7 +3223,7 @@ int ha_federated::external_lock(THD *thd, int lock_type)
DBUG_PRINT("info", ("error setting autocommit FALSE: %d", error));
DBUG_RETURN(error);
}
- thd->ha_data[ht->slot]= this;
+ thd_set_ha_data(thd, ht, this);
trans_register_ha(thd, TRUE, ht);
/*
Send a lock table to the remote end.
@@ -3230,7 +3253,7 @@ int ha_federated::external_lock(THD *thd, int lock_type)
static int federated_commit(handlerton *hton, THD *thd, bool all)
{
int return_val= 0;
- ha_federated *trx= (ha_federated *)thd->ha_data[hton->slot];
+ ha_federated *trx= (ha_federated *) thd_get_ha_data(thd, hton);
DBUG_ENTER("federated_commit");
if (all)
@@ -3245,7 +3268,7 @@ static int federated_commit(handlerton *hton, THD *thd, bool all)
if (error && !return_val)
return_val= error;
}
- thd->ha_data[hton->slot]= NULL;
+ thd_set_ha_data(thd, hton, NULL);
}
DBUG_PRINT("info", ("error val: %d", return_val));
@@ -3256,7 +3279,7 @@ static int federated_commit(handlerton *hton, THD *thd, bool all)
static int federated_rollback(handlerton *hton, THD *thd, bool all)
{
int return_val= 0;
- ha_federated *trx= (ha_federated *)thd->ha_data[hton->slot];
+ ha_federated *trx= (ha_federated *)thd_get_ha_data(thd, hton);
DBUG_ENTER("federated_rollback");
if (all)
@@ -3271,7 +3294,7 @@ static int federated_rollback(handlerton *hton, THD *thd, bool all)
if (error && !return_val)
return_val= error;
}
- thd->ha_data[hton->slot]= NULL;
+ thd_set_ha_data(thd, hton, NULL);
}
DBUG_PRINT("info", ("error val: %d", return_val));
diff --git a/storage/federated/ha_federated.h b/storage/federated/ha_federated.h
index 40bcf9cc402..1974f9936fc 100644
--- a/storage/federated/ha_federated.h
+++ b/storage/federated/ha_federated.h
@@ -210,6 +210,7 @@ public:
int update_row(const uchar *old_data, uchar *new_data);
int delete_row(const uchar *buf);
int index_init(uint keynr, bool sorted);
+ ha_rows estimate_rows_upper_bound();
int index_read(uchar *buf, const uchar *key,
uint key_len, enum ha_rkey_function find_flag);
int index_read_idx(uchar *buf, uint idx, const uchar *key,
diff --git a/storage/heap/ha_heap.cc b/storage/heap/ha_heap.cc
index 4934792de80..601d4612dda 100644
--- a/storage/heap/ha_heap.cc
+++ b/storage/heap/ha_heap.cc
@@ -197,7 +197,7 @@ void ha_heap::update_key_stats()
else
{
ha_rows hash_buckets= file->s->keydef[i].hash_buckets;
- uint no_records= hash_buckets ? file->s->records/hash_buckets : 2;
+ uint no_records= hash_buckets ? (uint) (file->s->records/hash_buckets) : 2;
if (no_records < 2)
no_records= 2;
key->rec_per_key[key->key_parts-1]= no_records;
diff --git a/storage/heap/hp_delete.c b/storage/heap/hp_delete.c
index 1dd79a42e0b..9e9e28da335 100644
--- a/storage/heap/hp_delete.c
+++ b/storage/heap/hp_delete.c
@@ -72,10 +72,7 @@ int hp_rb_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo,
int res;
if (flag)
- {
info->last_pos= NULL; /* For heap_rnext/heap_rprev */
- info->lastkey_len= 0;
- }
custom_arg.keyseg= keyinfo->seg;
custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
diff --git a/storage/heap/hp_rfirst.c b/storage/heap/hp_rfirst.c
index 48c1e625bd8..d0d2ec9b506 100644
--- a/storage/heap/hp_rfirst.c
+++ b/storage/heap/hp_rfirst.c
@@ -35,6 +35,17 @@ int heap_rfirst(HP_INFO *info, uchar *record, int inx)
sizeof(uchar*));
info->current_ptr = pos;
memcpy(record, pos, (size_t)share->reclength);
+ /*
+ If we're performing index_first on a table that was taken from
+ table cache, info->lastkey_len is initialized to previous query.
+ Thus we set info->lastkey_len to proper value for subsequent
+ heap_rnext() calls.
+ This is needed for DELETE queries only, otherwise this variable is
+ not used.
+ Note that the same workaround may be needed for heap_rlast(), but
+ for now heap_rlast() is never used for DELETE queries.
+ */
+ info->lastkey_len= 0;
info->update = HA_STATE_AKTIV;
}
else
diff --git a/storage/heap/hp_rnext.c b/storage/heap/hp_rnext.c
index 262754e9e64..3d715f4e6d3 100644
--- a/storage/heap/hp_rnext.c
+++ b/storage/heap/hp_rnext.c
@@ -33,11 +33,40 @@ int heap_rnext(HP_INFO *info, uchar *record)
heap_rb_param custom_arg;
if (info->last_pos)
+ {
+ /*
+ We enter this branch for non-DELETE queries after heap_rkey()
+ or heap_rfirst(). As last key position (info->last_pos) is available,
+ we only need to climb the tree using tree_search_next().
+ */
pos = tree_search_next(&keyinfo->rb_tree, &info->last_pos,
offsetof(TREE_ELEMENT, left),
offsetof(TREE_ELEMENT, right));
+ }
+ else if (!info->lastkey_len)
+ {
+ /*
+ We enter this branch only for DELETE queries after heap_rfirst(). E.g.
+ DELETE FROM t1 WHERE a<10. As last key position is not available
+ (last key is removed by heap_delete()), we must restart search as it
+ is done in heap_rfirst().
+
+ It should be safe to handle this situation without this branch. That is
+ branch below should find smallest element in a tree as lastkey_len is
+ zero. tree_search_edge() is a kind of optimisation here as it should be
+ faster than tree_search_key().
+ */
+ pos= tree_search_edge(&keyinfo->rb_tree, info->parents,
+ &info->last_pos, offsetof(TREE_ELEMENT, left));
+ }
else
{
+ /*
+ We enter this branch only for DELETE queries after heap_rkey(). E.g.
+ DELETE FROM t1 WHERE a=10. As last key position is not available
+ (last key is removed by heap_delete()), we must restart search as it
+ is done in heap_rkey().
+ */
custom_arg.keyseg = keyinfo->seg;
custom_arg.key_length = info->lastkey_len;
custom_arg.search_flag = SEARCH_SAME | SEARCH_FIND;
diff --git a/storage/innobase/buf/buf0lru.c b/storage/innobase/buf/buf0lru.c
index 7b49a7641af..f3913ed49b7 100644
--- a/storage/innobase/buf/buf0lru.c
+++ b/storage/innobase/buf/buf0lru.c
@@ -881,7 +881,7 @@ buf_LRU_block_free_non_file_page(
UT_LIST_ADD_FIRST(free, buf_pool->free, block);
block->in_free_list = TRUE;
- UNIV_MEM_FREE(block->frame, UNIV_PAGE_SIZE);
+ UNIV_MEM_ASSERT_AND_FREE(block->frame, UNIV_PAGE_SIZE);
if (srv_use_awe && block->frame) {
/* Add to the list of mapped pages */
diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c
index 595dfb06ee5..8aba5ac1c85 100644
--- a/storage/innobase/dict/dict0dict.c
+++ b/storage/innobase/dict/dict0dict.c
@@ -84,16 +84,6 @@ innobase_convert_from_id(
ulint len); /* in: length of 'to', in bytes;
should be at least 3 * strlen(to) + 1 */
/**********************************************************************
-Removes the filename encoding of a table or database name.
-
-NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
-this function, you MUST change also the prototype here! */
-extern
-void
-innobase_convert_from_filename(
-/*===========================*/
- char* s); /* in: identifier; out: decoded identifier */
-/**********************************************************************
Compares NUL-terminated UTF-8 strings case insensitively.
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
@@ -441,6 +431,8 @@ dict_table_autoinc_initialize(
dict_table_t* table, /* in: table */
ib_longlong value) /* in: next value to assign to a row */
{
+ ut_ad(mutex_own(&table->autoinc_mutex));
+
table->autoinc_inited = TRUE;
table->autoinc = value;
}
@@ -457,6 +449,8 @@ dict_table_autoinc_read(
{
ib_longlong value;
+ ut_ad(mutex_own(&table->autoinc_mutex));
+
if (!table->autoinc_inited) {
value = 0;
diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c
index 1ff1fd54cec..f594e64f517 100644
--- a/storage/innobase/dict/dict0load.c
+++ b/storage/innobase/dict/dict0load.c
@@ -551,11 +551,13 @@ dict_load_fields(
Loads definitions for table indexes. Adds them to the data dictionary
cache. */
static
-ibool
+ulint
dict_load_indexes(
/*==============*/
- /* out: TRUE if ok, FALSE if corruption
- of dictionary table */
+ /* out: DB_SUCCESS if ok, DB_CORRUPTION
+ if corruption of dictionary table or
+ DB_UNSUPPORTED if table has unknown index
+ type */
dict_table_t* table, /* in: table */
mem_heap_t* heap) /* in: memory heap for temporary storage */
{
@@ -578,6 +580,7 @@ dict_load_indexes(
ibool is_sys_table;
dulint id;
mtr_t mtr;
+ ulint error = DB_SUCCESS;
ut_ad(mutex_own(&(dict_sys->mutex)));
@@ -624,10 +627,8 @@ dict_load_indexes(
dict_load_report_deleted_index(table->name,
ULINT_UNDEFINED);
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
-
- return(FALSE);
+ error = DB_CORRUPTION;
+ goto func_exit;
}
field = rec_get_nth_field_old(rec, 1, &len);
@@ -653,7 +654,18 @@ dict_load_indexes(
field = rec_get_nth_field_old(rec, 8, &len);
page_no = mach_read_from_4(field);
- if (page_no == FIL_NULL) {
+ /* We check for unsupported types first, so that the
+ subsequent checks are relevant for the supported types. */
+ if (type & ~(DICT_CLUSTERED | DICT_UNIQUE)) {
+
+ fprintf(stderr,
+ "InnoDB: Error: unknown type %lu"
+ " of index %s of table %s\n",
+ (ulong) type, name_buf, table->name);
+
+ error = DB_UNSUPPORTED;
+ goto func_exit;
+ } else if (page_no == FIL_NULL) {
fprintf(stderr,
"InnoDB: Error: trying to load index %s"
@@ -661,14 +673,10 @@ dict_load_indexes(
"InnoDB: but the index tree has been freed!\n",
name_buf, table->name);
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
-
- return(FALSE);
- }
-
- if ((type & DICT_CLUSTERED) == 0
- && NULL == dict_table_get_first_index(table)) {
+ error = DB_CORRUPTION;
+ goto func_exit;
+ } else if ((type & DICT_CLUSTERED) == 0
+ && NULL == dict_table_get_first_index(table)) {
fprintf(stderr,
"InnoDB: Error: trying to load index %s"
@@ -677,18 +685,14 @@ dict_load_indexes(
" is not clustered!\n",
name_buf, table->name);
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
-
- return(FALSE);
- }
-
- if (is_sys_table
- && ((type & DICT_CLUSTERED)
- || ((table == dict_sys->sys_tables)
- && (name_len == (sizeof "ID_IND") - 1)
- && (0 == ut_memcmp(name_buf, "ID_IND",
- name_len))))) {
+ error = DB_CORRUPTION;
+ goto func_exit;
+ } else if (is_sys_table
+ && ((type & DICT_CLUSTERED)
+ || ((table == dict_sys->sys_tables)
+ && (name_len == (sizeof "ID_IND") - 1)
+ && (0 == ut_memcmp(name_buf,
+ "ID_IND", name_len))))) {
/* The index was created in memory already at booting
of the database server */
@@ -704,10 +708,11 @@ dict_load_indexes(
btr_pcur_move_to_next_user_rec(&pcur, &mtr);
}
+func_exit:
btr_pcur_close(&pcur);
mtr_commit(&mtr);
- return(TRUE);
+ return(error);
}
/************************************************************************
@@ -857,11 +862,20 @@ err_exit:
mem_heap_empty(heap);
- dict_load_indexes(table, heap);
-
- err = dict_load_foreigns(table->name, TRUE);
+ err = dict_load_indexes(table, heap);
+
+ /* If the force recovery flag is set, we open the table irrespective
+ of the error condition, since the user may want to dump data from the
+ clustered index. However we load the foreign key information only if
+ all indexes were loaded. */
+ if (err != DB_SUCCESS && !srv_force_recovery) {
+ dict_mem_table_free(table);
+ table = NULL;
+ } else if (err == DB_SUCCESS) {
+ err = dict_load_foreigns(table->name, TRUE);
+ }
#if 0
- if (err != DB_SUCCESS) {
+ if (err != DB_SUCCESS && table != NULL) {
mutex_enter(&dict_foreign_err_mutex);
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index f4f24b33f53..205cfc91e52 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -129,6 +129,7 @@ static my_bool innobase_locks_unsafe_for_binlog = FALSE;
static my_bool innobase_rollback_on_timeout = FALSE;
static my_bool innobase_create_status_file = FALSE;
static my_bool innobase_stats_on_metadata = TRUE;
+static my_bool innobase_use_adaptive_hash_indexes = TRUE;
static char* internal_innobase_data_file_path = NULL;
@@ -602,7 +603,6 @@ convert_error_code_to_mysql(
thd_mark_transaction_to_rollback(thd, TRUE);
return(HA_ERR_LOCK_DEADLOCK);
-
} else if (error == (int) DB_LOCK_WAIT_TIMEOUT) {
/* Starting from 5.0.13, we let MySQL just roll back the
@@ -679,6 +679,9 @@ convert_error_code_to_mysql(
return(HA_ERR_RECORD_FILE_FULL);
#endif
+ } else if (error == DB_UNSUPPORTED) {
+
+ return(HA_ERR_UNSUPPORTED);
} else {
return(-1); // Unknown error
}
@@ -802,23 +805,6 @@ innobase_convert_from_id(
}
/**********************************************************************
-Removes the filename encoding of a table or database name.
-
-NOTE that the exact prototype of this function has to be in
-/innobase/dict/dict0dict.c! */
-extern "C"
-void
-innobase_convert_from_filename(
-/*===========================*/
- char* s) /* in: identifier; out: decoded identifier */
-{
- uint errors;
-
- strconvert(&my_charset_filename, s,
- system_charset_info, s, strlen(s), &errors);
-}
-
-/**********************************************************************
Compares NUL-terminated UTF-8 strings case insensitively.
NOTE that the exact prototype of this function has to be in
@@ -1144,7 +1130,6 @@ innobase_query_caching_of_table_permitted(
}
if (trx->has_search_latch) {
- ut_print_timestamp(stderr);
sql_print_error("The calling thread is holding the adaptive "
"search, latch though calling "
"innobase_query_caching_of_table_permitted.");
@@ -1247,8 +1232,7 @@ innobase_invalidate_query_cache(
}
/*********************************************************************
-Display an SQL identifier.
-This definition must match the one in innobase/ut/ut0ut.c! */
+Display an SQL identifier. */
extern "C"
void
innobase_print_identifier(
@@ -1636,6 +1620,9 @@ innobase_init(
srv_stats_on_metadata = (ibool) innobase_stats_on_metadata;
+ srv_use_adaptive_hash_indexes =
+ (ibool) innobase_use_adaptive_hash_indexes;
+
srv_print_verbose_log = mysqld_embedded ? 0 : 1;
/* Store the default charset-collation number of this MySQL
@@ -1904,12 +1891,11 @@ retry:
/* We just mark the SQL statement ended and do not do a
transaction commit */
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some
- table in this SQL statement we release it now */
+ /* If we had reserved the auto-inc lock for some
+ table in this SQL statement we release it now */
+
+ row_unlock_table_autoinc_for_mysql(trx);
- row_unlock_table_autoinc_for_mysql(trx);
- }
/* Store the current undo_no of the transaction so that we
know where to roll back if we have to roll back the next
SQL statement */
@@ -1962,13 +1948,11 @@ innobase_rollback(
innobase_release_stat_resources(trx);
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some table (if
- we come here to roll back the latest SQL statement) we
- release it now before a possibly lengthy rollback */
+ /* If we had reserved the auto-inc lock for some table (if
+ we come here to roll back the latest SQL statement) we
+ release it now before a possibly lengthy rollback */
- row_unlock_table_autoinc_for_mysql(trx);
- }
+ row_unlock_table_autoinc_for_mysql(trx);
if (all
|| !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
@@ -2002,13 +1986,11 @@ innobase_rollback_trx(
innobase_release_stat_resources(trx);
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some table (if
- we come here to roll back the latest SQL statement) we
- release it now before a possibly lengthy rollback */
+ /* If we had reserved the auto-inc lock for some table (if
+ we come here to roll back the latest SQL statement) we
+ release it now before a possibly lengthy rollback */
- row_unlock_table_autoinc_for_mysql(trx);
- }
+ row_unlock_table_autoinc_for_mysql(trx);
error = trx_rollback_for_mysql(trx);
@@ -2325,14 +2307,18 @@ ha_innobase::open(
ib_table = dict_table_get(norm_name, TRUE);
if (NULL == ib_table) {
- ut_print_timestamp(stderr);
- sql_print_error("Cannot find table %s from the internal data "
- "dictionary\nof InnoDB though the .frm file "
- "for the table exists. Maybe you\nhave "
- "deleted and recreated InnoDB data files but "
- "have forgotten\nto delete the corresponding "
- ".frm files of InnoDB tables, or you\n"
- "have moved .frm files to another database?\n"
+ sql_print_error("Cannot find or open table %s from\n"
+ "the internal data dictionary of InnoDB "
+ "though the .frm file for the\n"
+ "table exists. Maybe you have deleted and "
+ "recreated InnoDB data\n"
+ "files but have forgotten to delete the "
+ "corresponding .frm files\n"
+ "of InnoDB tables, or you have moved .frm "
+ "files to another database?\n"
+ "or, the table contains indexes that this "
+ "version of the engine\n"
+ "doesn't support.\n"
"See http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n"
"how you can resolve the problem.\n",
norm_name);
@@ -2344,7 +2330,6 @@ ha_innobase::open(
}
if (ib_table->ibd_file_missing && !thd_tablespace_op(thd)) {
- ut_print_timestamp(stderr);
sql_print_error("MySQL is trying to open a table handle but "
"the .ibd file for\ntable %s does not exist.\n"
"Have you deleted the .ibd file from the "
@@ -3418,7 +3403,7 @@ no_commit:
/*
ut_print_timestamp(stderr);
fprintf(stderr,
- " InnoDB error: ALTER TABLE is holding lock"
+ " InnoDB: ALTER TABLE is holding lock"
" on %lu tables!\n",
prebuilt->trx->mysql_n_tables_locked);
*/
@@ -3482,6 +3467,7 @@ no_commit:
/* Handle duplicate key errors */
if (auto_inc_used) {
+ ulint err;
ulonglong auto_inc;
/* Note the number of rows processed for this statement, used
@@ -3535,7 +3521,11 @@ set_max_autoinc:
ut_a(prebuilt->table->autoinc_increment > 0);
auto_inc += prebuilt->table->autoinc_increment;
- innobase_set_max_autoinc(auto_inc);
+ err = innobase_set_max_autoinc(auto_inc);
+
+ if (err != DB_SUCCESS) {
+ error = (int) err;
+ }
}
break;
}
@@ -3771,7 +3761,7 @@ ha_innobase::update_row(
if (auto_inc != 0) {
auto_inc += prebuilt->table->autoinc_increment;
- innobase_set_max_autoinc(auto_inc);
+ error = innobase_set_max_autoinc(auto_inc);
}
}
@@ -3958,33 +3948,51 @@ convert_search_mode_to_innobase(
enum ha_rkey_function find_flag)
{
switch (find_flag) {
- case HA_READ_KEY_EXACT: return(PAGE_CUR_GE);
- /* the above does not require the index to be UNIQUE */
- case HA_READ_KEY_OR_NEXT: return(PAGE_CUR_GE);
- case HA_READ_KEY_OR_PREV: return(PAGE_CUR_LE);
- case HA_READ_AFTER_KEY: return(PAGE_CUR_G);
- case HA_READ_BEFORE_KEY: return(PAGE_CUR_L);
- case HA_READ_PREFIX: return(PAGE_CUR_GE);
- case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE);
- case HA_READ_PREFIX_LAST_OR_PREV:return(PAGE_CUR_LE);
- /* In MySQL-4.0 HA_READ_PREFIX and HA_READ_PREFIX_LAST always
- pass a complete-field prefix of a key value as the search
- tuple. I.e., it is not allowed that the last field would
- just contain n first bytes of the full field value.
- MySQL uses a 'padding' trick to convert LIKE 'abc%'
- type queries so that it can use as a search tuple
- a complete-field-prefix of a key value. Thus, the InnoDB
- search mode PAGE_CUR_LE_OR_EXTENDS is never used.
- TODO: when/if MySQL starts to use also partial-field
- prefixes, we have to deal with stripping of spaces
- and comparison of non-latin1 char type fields in
- innobase_mysql_cmp() to get PAGE_CUR_LE_OR_EXTENDS to
- work correctly. */
-
- default: assert(0);
- }
-
- return(0);
+ case HA_READ_KEY_EXACT:
+ /* this does not require the index to be UNIQUE */
+ return(PAGE_CUR_GE);
+ case HA_READ_KEY_OR_NEXT:
+ return(PAGE_CUR_GE);
+ case HA_READ_KEY_OR_PREV:
+ return(PAGE_CUR_LE);
+ case HA_READ_AFTER_KEY:
+ return(PAGE_CUR_G);
+ case HA_READ_BEFORE_KEY:
+ return(PAGE_CUR_L);
+ case HA_READ_PREFIX:
+ return(PAGE_CUR_GE);
+ case HA_READ_PREFIX_LAST:
+ return(PAGE_CUR_LE);
+ case HA_READ_PREFIX_LAST_OR_PREV:
+ return(PAGE_CUR_LE);
+ /* In MySQL-4.0 HA_READ_PREFIX and HA_READ_PREFIX_LAST always
+ pass a complete-field prefix of a key value as the search
+ tuple. I.e., it is not allowed that the last field would
+ just contain n first bytes of the full field value.
+ MySQL uses a 'padding' trick to convert LIKE 'abc%'
+ type queries so that it can use as a search tuple
+ a complete-field-prefix of a key value. Thus, the InnoDB
+ search mode PAGE_CUR_LE_OR_EXTENDS is never used.
+ TODO: when/if MySQL starts to use also partial-field
+ prefixes, we have to deal with stripping of spaces
+ and comparison of non-latin1 char type fields in
+ innobase_mysql_cmp() to get PAGE_CUR_LE_OR_EXTENDS to
+ work correctly. */
+ case HA_READ_MBR_CONTAIN:
+ case HA_READ_MBR_INTERSECT:
+ case HA_READ_MBR_WITHIN:
+ case HA_READ_MBR_DISJOINT:
+ case HA_READ_MBR_EQUAL:
+ my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0));
+ return(PAGE_CUR_UNSUPP);
+ /* do not use "default:" in order to produce a gcc warning:
+ enumeration value '...' not handled in switch
+ (if -Wswitch or -Wall is used) */
+ }
+
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "this functionality");
+
+ return(PAGE_CUR_UNSUPP);
}
/*
@@ -4112,11 +4120,18 @@ ha_innobase::index_read(
last_match_mode = (uint) match_mode;
- innodb_srv_conc_enter_innodb(prebuilt->trx);
+ if (mode != PAGE_CUR_UNSUPP) {
- ret = row_search_for_mysql((byte*) buf, mode, prebuilt, match_mode, 0);
+ innodb_srv_conc_enter_innodb(prebuilt->trx);
- innodb_srv_conc_exit_innodb(prebuilt->trx);
+ ret = row_search_for_mysql((byte*) buf, mode, prebuilt,
+ match_mode, 0);
+
+ innodb_srv_conc_exit_innodb(prebuilt->trx);
+ } else {
+
+ ret = DB_UNSUPPORTED;
+ }
if (ret == DB_SUCCESS) {
error = 0;
@@ -5466,8 +5481,16 @@ ha_innobase::records_in_range(
mode2 = convert_search_mode_to_innobase(max_key ? max_key->flag :
HA_READ_KEY_EXACT);
- n_rows = btr_estimate_n_rows_in_range(index, range_start,
- mode1, range_end, mode2);
+ if (mode1 != PAGE_CUR_UNSUPP && mode2 != PAGE_CUR_UNSUPP) {
+
+ n_rows = btr_estimate_n_rows_in_range(index, range_start,
+ mode1, range_end,
+ mode2);
+ } else {
+
+ n_rows = 0;
+ }
+
dtuple_free_for_mysql(heap1);
dtuple_free_for_mysql(heap2);
@@ -5716,7 +5739,6 @@ ha_innobase::info(
for (i = 0; i < table->s->keys; i++) {
if (index == NULL) {
- ut_print_timestamp(stderr);
sql_print_error("Table %s contains fewer "
"indexes inside InnoDB than "
"are defined in the MySQL "
@@ -5732,7 +5754,6 @@ ha_innobase::info(
for (j = 0; j < table->key_info[i].key_parts; j++) {
if (j + 1 > index->n_uniq) {
- ut_print_timestamp(stderr);
sql_print_error(
"Index %s of %s has %lu columns unique inside InnoDB, but MySQL is asking "
"statistics for %lu columns. Have you mixed up .frm files from different "
@@ -5766,7 +5787,7 @@ ha_innobase::info(
table->key_info[i].rec_per_key[j]=
rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 :
- rec_per_key;
+ (ulong) rec_per_key;
}
index = dict_table_get_next_index_noninline(index);
@@ -5797,7 +5818,6 @@ ha_innobase::info(
ret = innobase_read_and_init_auto_inc(&auto_inc);
if (ret != 0) {
- ut_print_timestamp(stderr);
sql_print_error("Cannot get table %s auto-inc"
"counter value in ::info\n",
ib_table->name);
@@ -6310,6 +6330,9 @@ ha_innobase::start_stmt(
innobase_release_stat_resources(trx);
+ /* Reset the AUTOINC statement level counter for multi-row INSERTs. */
+ trx->n_autoinc_rows = 0;
+
prebuilt->sql_stat_start = TRUE;
prebuilt->hint_need_to_fetch_extra_cols = 0;
reset_template(prebuilt);
@@ -6452,9 +6475,6 @@ ha_innobase::external_lock(
innobase_register_stmt(ht, thd);
}
- trx->n_mysql_tables_in_use++;
- prebuilt->mysql_has_locked = TRUE;
-
if (trx->isolation_level == TRX_ISO_SERIALIZABLE
&& prebuilt->select_lock_type == LOCK_NONE
&& thd_test_options(thd,
@@ -6503,6 +6523,9 @@ ha_innobase::external_lock(
trx->mysql_n_tables_locked++;
}
+ trx->n_mysql_tables_in_use++;
+ prebuilt->mysql_has_locked = TRUE;
+
DBUG_RETURN(0);
}
@@ -6568,14 +6591,17 @@ ha_innobase::transactional_table_lock(
if (prebuilt->table->ibd_file_missing && !thd_tablespace_op(thd)) {
ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"MySQL is trying to use a table handle but the .ibd file for\n"
-"table %s does not exist.\n"
-"Have you deleted the .ibd file from the database directory under\n"
-"the MySQL datadir?"
-"See http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n"
-"how you can resolve the problem.\n",
- prebuilt->table->name);
+ fprintf(stderr,
+ " InnoDB: MySQL is trying to use a table handle"
+ " but the .ibd file for\n"
+ "InnoDB: table %s does not exist.\n"
+ "InnoDB: Have you deleted the .ibd file"
+ " from the database directory under\n"
+ "InnoDB: the MySQL datadir?"
+ "InnoDB: See"
+ " http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n"
+ "InnoDB: how you can resolve the problem.\n",
+ prebuilt->table->name);
DBUG_RETURN(HA_ERR_CRASHED);
}
@@ -7126,8 +7152,8 @@ the value of the auto-inc counter. */
int
ha_innobase::innobase_read_and_init_auto_inc(
/*=========================================*/
- /* out: 0 or error code:
- deadlock or lock wait timeout */
+ /* out: 0 or generic MySQL
+ error code */
longlong* value) /* out: the autoinc value */
{
longlong auto_inc;
@@ -7168,7 +7194,7 @@ ha_innobase::innobase_read_and_init_auto_inc(
if (auto_inc == 0) {
dict_index_t* index;
- ulint error = DB_SUCCESS;
+ ulint error;
const char* autoinc_col_name;
ut_a(!innodb_table->autoinc_inited);
@@ -7184,9 +7210,10 @@ ha_innobase::innobase_read_and_init_auto_inc(
++auto_inc;
dict_table_autoinc_initialize(innodb_table, auto_inc);
} else {
- fprintf(stderr, " InnoDB error: Couldn't read the "
- "max AUTOINC value from index (%s).\n",
- index->name);
+ ut_print_timestamp(stderr);
+ fprintf(stderr, " InnoDB: Error: (%lu) Couldn't read "
+ "the max AUTOINC value from the index (%s).\n",
+ error, index->name);
mysql_error = 1;
}
@@ -7223,6 +7250,11 @@ ha_innobase::innobase_get_auto_increment(
{
ulong error;
+ *value = 0;
+
+ /* Note: If the table is not initialized when we attempt the
+ read below. We initialize the table's auto-inc counter and
+ always do a reread of the AUTOINC value. */
do {
error = innobase_autoinc_lock();
@@ -7240,12 +7272,10 @@ ha_innobase::innobase_get_auto_increment(
trx = prebuilt->trx;
dict_table_autoinc_unlock(prebuilt->table);
- if (trx->auto_inc_lock) {
- /* If we had reserved the AUTO-INC
- lock in this SQL statement we release
- it before retrying.*/
- row_unlock_table_autoinc_for_mysql(trx);
- }
+ /* If we had reserved the AUTO-INC
+ lock in this SQL statement we release
+ it before retrying.*/
+ row_unlock_table_autoinc_for_mysql(trx);
/* Just to make sure */
ut_a(!trx->auto_inc_lock);
@@ -7264,7 +7294,15 @@ ha_innobase::innobase_get_auto_increment(
} else {
*value = (ulonglong) autoinc;
}
+ /* A deadlock error during normal processing is OK
+ and can be ignored. */
+ } else if (error != DB_DEADLOCK) {
+
+ sql_print_error("InnoDB: Error: %lu in "
+ "::innobase_get_auto_increment()",
+ error);
}
+
} while (*value == 0 && error == DB_SUCCESS);
return(error);
@@ -7280,13 +7318,14 @@ we have a table-level lock). offset, increment, nb_desired_values are ignored.
void
ha_innobase::get_auto_increment(
-/*=================================*/
+/*============================*/
ulonglong offset, /* in: */
ulonglong increment, /* in: table autoinc increment */
ulonglong nb_desired_values, /* in: number of values reqd */
ulonglong *first_value, /* out: the autoinc value */
ulonglong *nb_reserved_values) /* out: count of reserved values */
{
+ trx_t* trx;
ulint error;
ulonglong autoinc = 0;
@@ -7296,13 +7335,6 @@ ha_innobase::get_auto_increment(
error = innobase_get_auto_increment(&autoinc);
if (error != DB_SUCCESS) {
- /* This should never happen in the code > ver 5.0.6,
- since we call this function only after the counter
- has been initialized. */
-
- ut_print_timestamp(stderr);
- sql_print_error("Error %lu in ::get_auto_increment()", error);
-
*first_value = (~(ulonglong) 0);
return;
}
@@ -7313,37 +7345,34 @@ ha_innobase::get_auto_increment(
this method for the same statement results in different values which
don't make sense. Therefore we store the value the first time we are
called and count down from that as rows are written (see write_row()).
+ */
- We make one exception, if the *first_value is precomputed by MySQL
- we use that value. And set the number of reserved values to 1 if
- this is the first time we were called for the SQL statement, this
- will force MySQL to call us for the next value. If we are in the
- middle of a multi-row insert we preserve the existing counter.*/
- if (*first_value == 0) {
-
- /* Called for the first time ? */
- if (prebuilt->trx->n_autoinc_rows == 0) {
+ trx = prebuilt->trx;
- prebuilt->trx->n_autoinc_rows = (ulint) nb_desired_values;
+ /* Note: We can't rely on *first_value since some MySQL engines,
+ in particular the partition engine, don't initialize it to 0 when
+ invoking this method. So we are not sure if it's guaranteed to
+ be 0 or not. */
- /* It's possible for nb_desired_values to be 0:
- e.g., INSERT INTO T1(C) SELECT C FROM T2; */
- if (nb_desired_values == 0) {
+ /* Called for the first time ? */
+ if (trx->n_autoinc_rows == 0) {
- ++prebuilt->trx->n_autoinc_rows;
- }
- }
+ trx->n_autoinc_rows = (ulint) nb_desired_values;
- *first_value = autoinc;
+ /* It's possible for nb_desired_values to be 0:
+ e.g., INSERT INTO T1(C) SELECT C FROM T2; */
+ if (nb_desired_values == 0) {
- } else if (prebuilt->trx->n_autoinc_rows == 0) {
+ trx->n_autoinc_rows = 1;
+ }
- prebuilt->trx->n_autoinc_rows = 1;
+ set_if_bigger(*first_value, autoinc);
+ /* Not in the middle of a mult-row INSERT. */
+ } else if (prebuilt->last_value == 0) {
+ set_if_bigger(*first_value, autoinc);
}
- ut_a(prebuilt->trx->n_autoinc_rows > 0);
-
- *nb_reserved_values = prebuilt->trx->n_autoinc_rows;
+ *nb_reserved_values = trx->n_autoinc_rows;
/* With old style AUTOINC locking we only update the table's
AUTOINC counter after attempting to insert the row. */
@@ -7374,7 +7403,9 @@ ha_innobase::get_auto_increment(
/* See comment in handler.h */
int
-ha_innobase::reset_auto_increment(ulonglong value)
+ha_innobase::reset_auto_increment(
+/*==============================*/
+ ulonglong value) /* in: new value for table autoinc */
{
DBUG_ENTER("ha_innobase::reset_auto_increment");
@@ -7670,12 +7701,10 @@ innobase_xa_prepare(
/* We just mark the SQL statement ended and do not do a
transaction prepare */
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some
- table in this SQL statement we release it now */
+ /* If we had reserved the auto-inc lock for some
+ table in this SQL statement we release it now */
- row_unlock_table_autoinc_for_mysql(trx);
- }
+ row_unlock_table_autoinc_for_mysql(trx);
/* Store the current undo_no of the transaction so that we
know where to roll back if we have to roll back the next
@@ -7940,6 +7969,11 @@ static MYSQL_SYSVAR_BOOL(stats_on_metadata, innobase_stats_on_metadata,
"Enable statistics gathering for metadata commands such as SHOW TABLE STATUS (on by default)",
NULL, NULL, TRUE);
+static MYSQL_SYSVAR_BOOL(use_adaptive_hash_indexes, innobase_use_adaptive_hash_indexes,
+ PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
+ "Enable the InnoDB adaptive hash indexes (enabled by default)",
+ NULL, NULL, TRUE);
+
static MYSQL_SYSVAR_LONG(additional_mem_pool_size, innobase_additional_mem_pool_size,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"Size of a memory pool InnoDB uses to store data dictionary information and other internal data structures.",
@@ -8026,7 +8060,7 @@ static MYSQL_SYSVAR_STR(data_file_path, innobase_data_file_path,
NULL, NULL, NULL);
static MYSQL_SYSVAR_LONG(autoinc_lock_mode, innobase_autoinc_lock_mode,
- PLUGIN_VAR_RQCMDARG,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"The AUTOINC lock modes supported by InnoDB:\n"
" 0 => Old style AUTOINC locking (for backward compatibility)\n"
" 1 => New style AUTOINC locking\n"
@@ -8068,6 +8102,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(open_files),
MYSQL_SYSVAR(rollback_on_timeout),
MYSQL_SYSVAR(stats_on_metadata),
+ MYSQL_SYSVAR(use_adaptive_hash_indexes),
MYSQL_SYSVAR(status_file),
MYSQL_SYSVAR(support_xa),
MYSQL_SYSVAR(sync_spin_loops),
diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h
index fe5ebd57990..773884b6584 100644
--- a/storage/innobase/handler/ha_innodb.h
+++ b/storage/innobase/handler/ha_innodb.h
@@ -250,17 +250,3 @@ int thd_binlog_format(const MYSQL_THD thd);
*/
void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all);
}
-
-/*
- don't delete it - it may be re-enabled later
- as an optimization for the most common case InnoDB+binlog
-*/
-#if 0
-int innobase_report_binlog_offset_and_commit(
- THD* thd,
- void* trx_handle,
- char* log_file_name,
- my_off_t end_offset);
-int innobase_commit_complete(void* trx_handle);
-void innobase_store_binlog_offset_and_flush_log(char *binlog_name,longlong offset);
-#endif
diff --git a/storage/innobase/ibuf/ibuf0ibuf.c b/storage/innobase/ibuf/ibuf0ibuf.c
index 44972356304..126fd9fdd33 100644
--- a/storage/innobase/ibuf/ibuf0ibuf.c
+++ b/storage/innobase/ibuf/ibuf0ibuf.c
@@ -2900,7 +2900,7 @@ dump:
ut_print_timestamp(stderr);
fprintf(stderr,
- "InnoDB: Error: Insert buffer insert"
+ " InnoDB: Error: Insert buffer insert"
" fails; page free %lu,"
" dtuple size %lu\n",
(ulong) page_get_max_insert_size(
@@ -2925,7 +2925,7 @@ dump:
buf_frame_get_page_no(page),
IBUF_BITMAP_FREE, mtr);
- fprintf(stderr, "Bitmap bits %lu\n",
+ fprintf(stderr, "InnoDB: Bitmap bits %lu\n",
(ulong) old_bits);
fputs("InnoDB: Submit a detailed bug report"
diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h
index 0aa1b87e470..ed7ce151718 100644
--- a/storage/innobase/include/db0err.h
+++ b/storage/innobase/include/db0err.h
@@ -61,12 +61,14 @@ Created 5/24/1996 Heikki Tuuri
activated by the operation would
lead to a duplicate key in some
table */
-
#define DB_TOO_MANY_CONCURRENT_TRXS 47 /* when InnoDB runs out of the
preconfigured undo slots, this can
only happen when there are too many
concurrent transactions */
-
+#define DB_UNSUPPORTED 48 /* when InnoDB sees any artefact or
+ a feature that it can't recoginize or
+ work with e.g., FT indexes created by
+ a later version of the engine. */
/* The following are partial failure codes */
#define DB_FAIL 1000
#define DB_OVERFLOW 1001
diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h
index 7fb50988941..ef0722321af 100644
--- a/storage/innobase/include/ha_prototypes.h
+++ b/storage/innobase/include/ha_prototypes.h
@@ -1,6 +1,8 @@
#ifndef HA_INNODB_PROTOTYPES_H
#define HA_INNODB_PROTOTYPES_H
+#ifndef UNIV_HOTBACKUP
+
#include "univ.i" /* ulint, uint */
#include "m_ctype.h" /* CHARSET_INFO */
@@ -22,6 +24,19 @@ innobase_convert_string(
CHARSET_INFO* from_cs,
uint* errors);
+/*********************************************************************
+Display an SQL identifier. */
+
+void
+innobase_print_identifier(
+/*======================*/
+ FILE* f, /* in: output stream */
+ trx_t* trx, /* in: transaction */
+ ibool table_id,/* in: TRUE=print a table name,
+ FALSE=print other identifier */
+ const char* name, /* in: name to print */
+ ulint namelen);/* in: length of name */
+
/**********************************************************************
Returns true if the thread is the replication thread on the slave
server. Used in srv_conc_enter_innodb() to determine if the thread
@@ -49,3 +64,4 @@ thd_has_edited_nontrans_tables(
void* thd); /* in: thread handle (THD*) */
#endif
+#endif
diff --git a/storage/innobase/include/mach0data.h b/storage/innobase/include/mach0data.h
index 8377114a723..37d862cc678 100644
--- a/storage/innobase/include/mach0data.h
+++ b/storage/innobase/include/mach0data.h
@@ -327,6 +327,17 @@ mach_write_to_2_little_endian(
byte* dest, /* in: where to write */
ulint n); /* in: unsigned long int to write */
+/*************************************************************
+Convert integral type from storage byte order (big endian) to
+host byte order. */
+UNIV_INLINE
+void
+mach_read_int_type(
+/*===============*/
+ byte* dest, /* out: where to write */
+ const byte* src, /* in: where to read from */
+ ulint len, /* in: length of src */
+ ibool unsigned_type); /* in: signed or unsigned flag */
#ifndef UNIV_NONINL
#include "mach0data.ic"
#endif
diff --git a/storage/innobase/include/mach0data.ic b/storage/innobase/include/mach0data.ic
index 03ece7529a8..64fb87f56ed 100644
--- a/storage/innobase/include/mach0data.ic
+++ b/storage/innobase/include/mach0data.ic
@@ -7,6 +7,8 @@ to the machine format.
Created 11/28/1995 Heikki Tuuri
***********************************************************************/
+#include "ut0mem.h"
+
/***********************************************************
The following function is used to store data in one byte. */
UNIV_INLINE
@@ -689,3 +691,38 @@ mach_write_to_2_little_endian(
*dest = (byte)(n & 0xFFUL);
}
+
+/*************************************************************
+Convert integral type from storage byte order (big endian) to
+host byte order. */
+UNIV_INLINE
+void
+mach_read_int_type(
+/*===============*/
+ byte* dest, /* out: where to write */
+ const byte* src, /* in: where to read from */
+ ulint len, /* in: length of src */
+ ibool unsigned_type) /* in: signed or unsigned flag */
+{
+#ifdef WORDS_BIGENDIAN
+ memcpy(dest, src, len);
+
+ if (!unsigned_type) {
+ dest[0] ^= 128;
+ }
+#else
+ byte* ptr;
+
+ /* Convert integer data from Innobase to a little-endian format,
+ sign bit restored to normal. */
+
+ for (ptr = dest + len; ptr != dest; ++src) {
+ --ptr;
+ *ptr = *src;
+ }
+
+ if (!unsigned_type) {
+ dest[len - 1] ^= 128;
+ }
+#endif
+}
diff --git a/storage/innobase/include/mem0dbg.h b/storage/innobase/include/mem0dbg.h
index 36cd7a89565..2393e4edb54 100644
--- a/storage/innobase/include/mem0dbg.h
+++ b/storage/innobase/include/mem0dbg.h
@@ -60,6 +60,14 @@ mem_heap_validate_or_print(
ulint* n_blocks); /* out: number of blocks in the heap,
if a NULL pointer is passed as this
argument, it is ignored */
+/******************************************************************
+Validates the contents of a memory heap. */
+
+ibool
+mem_heap_validate(
+/*==============*/
+ /* out: TRUE if ok */
+ mem_heap_t* heap); /* in: memory heap */
#endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */
#ifdef UNIV_DEBUG
/******************************************************************
@@ -71,14 +79,6 @@ mem_heap_check(
/* out: TRUE if ok */
mem_heap_t* heap); /* in: memory heap */
#endif /* UNIV_DEBUG */
-/******************************************************************
-Validates the contents of a memory heap. */
-
-ibool
-mem_heap_validate(
-/*==============*/
- /* out: TRUE if ok */
- mem_heap_t* heap); /* in: memory heap */
#ifdef UNIV_MEM_DEBUG
/*********************************************************************
TRUE if no memory is currently allocated. */
diff --git a/storage/innobase/include/mem0mem.ic b/storage/innobase/include/mem0mem.ic
index adae9ad8a33..6227a27f277 100644
--- a/storage/innobase/include/mem0mem.ic
+++ b/storage/innobase/include/mem0mem.ic
@@ -271,19 +271,16 @@ mem_heap_free_heap_top(
ut_ad(mem_block_get_start(block) <= mem_block_get_free(block));
/* In the debug version erase block from top up */
- {
- ulint len = (byte*)block + block->len - old_top;
- mem_erase_buf(old_top, len);
- UNIV_MEM_FREE(old_top, len);
- }
+ mem_erase_buf(old_top, (byte*)block + block->len - old_top);
/* Update allocated memory count */
mutex_enter(&mem_hash_mutex);
mem_current_allocated_memory -= (total_size - size);
mutex_exit(&mem_hash_mutex);
#else /* UNIV_MEM_DEBUG */
- UNIV_MEM_FREE(old_top, (byte*)block + block->len - old_top);
+ UNIV_MEM_ASSERT_W(old_top, (byte*)block + block->len - old_top);
#endif /* UNIV_MEM_DEBUG */
+ UNIV_MEM_ALLOC(old_top, (byte*)block + block->len - old_top);
/* If free == start, we may free the block if it is not the first
one */
@@ -363,6 +360,7 @@ mem_heap_free_top(
/* Subtract the free field of block */
mem_block_set_free(block, mem_block_get_free(block)
- MEM_SPACE_NEEDED(n));
+ UNIV_MEM_ASSERT_W((byte*) block + mem_block_get_free(block), n);
#ifdef UNIV_MEM_DEBUG
ut_ad(mem_block_get_start(block) <= mem_block_get_free(block));
@@ -378,7 +376,11 @@ mem_heap_free_top(
== mem_block_get_start(block))) {
mem_heap_block_free(heap, block);
} else {
- UNIV_MEM_FREE((byte*) block + mem_block_get_free(block), n);
+ /* Avoid a bogus UNIV_MEM_ASSERT_W() warning in a
+ subsequent invocation of mem_heap_free_top().
+ Originally, this was UNIV_MEM_FREE(), to catch writes
+ to freed memory. */
+ UNIV_MEM_ALLOC((byte*) block + mem_block_get_free(block), n);
}
}
diff --git a/storage/innobase/include/page0cur.h b/storage/innobase/include/page0cur.h
index 36370201d9b..04f731414a3 100644
--- a/storage/innobase/include/page0cur.h
+++ b/storage/innobase/include/page0cur.h
@@ -22,6 +22,7 @@ Created 10/4/1994 Heikki Tuuri
/* Page cursor search modes; the values must be in this order! */
+#define PAGE_CUR_UNSUPP 0
#define PAGE_CUR_G 1
#define PAGE_CUR_GE 2
#define PAGE_CUR_L 3
diff --git a/storage/innobase/include/rem0rec.ic b/storage/innobase/include/rem0rec.ic
index 95aa65fabba..5a4a0a0b5df 100644
--- a/storage/innobase/include/rem0rec.ic
+++ b/storage/innobase/include/rem0rec.ic
@@ -801,6 +801,7 @@ rec_offs_set_n_alloc(
{
ut_ad(offsets);
ut_ad(n_alloc > 0);
+ UNIV_MEM_ASSERT_AND_ALLOC(offsets, n_alloc * sizeof *offsets);
offsets[0] = n_alloc;
}
diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h
index aabb7f5f047..fd7ec8918ee 100644
--- a/storage/innobase/include/row0mysql.h
+++ b/storage/innobase/include/row0mysql.h
@@ -319,9 +319,11 @@ row_mysql_unfreeze_data_dictionary(
/*===============================*/
trx_t* trx); /* in: transaction */
/*************************************************************************
-Does a table creation operation for MySQL. If the name of the created
-table ends to characters INNODB_MONITOR, then this also starts
-printing of monitor output by the master thread. */
+Drops a table for MySQL. If the name of the table ends in
+one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor",
+"innodb_table_monitor", then this will also start the printing of monitor
+output by the master thread. If the table name ends in "innodb_mem_validate",
+InnoDB will try to invoke mem_validate(). */
int
row_create_table_for_mysql(
@@ -399,8 +401,9 @@ row_truncate_table_for_mysql(
dict_table_t* table, /* in: table handle */
trx_t* trx); /* in: transaction handle */
/*************************************************************************
-Drops a table for MySQL. If the name of the dropped table ends to
-characters INNODB_MONITOR, then this also stops printing of monitor
+Drops a table for MySQL. If the name of the dropped table ends in
+one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor",
+"innodb_table_monitor", then this will also stop the printing of monitor
output by the master thread. */
int
diff --git a/storage/innobase/include/sync0rw.h b/storage/innobase/include/sync0rw.h
index abf04253141..7d2f63803d0 100644
--- a/storage/innobase/include/sync0rw.h
+++ b/storage/innobase/include/sync0rw.h
@@ -101,6 +101,7 @@ void
rw_lock_free(
/*=========*/
rw_lock_t* lock); /* in: rw-lock */
+#ifdef UNIV_DEBUG
/**********************************************************************
Checks that the rw-lock has been initialized and that there are no
simultaneous shared and exclusive locks. */
@@ -109,6 +110,7 @@ ibool
rw_lock_validate(
/*=============*/
rw_lock_t* lock);
+#endif /* UNIV_DEBUG */
/******************************************************************
NOTE! The following macros should be used in rw s-locking, not the
corresponding function. */
diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i
index ba8e6e56219..8163ae16e4e 100644
--- a/storage/innobase/include/univ.i
+++ b/storage/innobase/include/univ.i
@@ -308,11 +308,39 @@ typedef void* os_thread_ret_t;
# define UNIV_MEM_INVALID(addr, size) VALGRIND_MAKE_MEM_UNDEFINED(addr, size)
# define UNIV_MEM_FREE(addr, size) VALGRIND_MAKE_MEM_NOACCESS(addr, size)
# define UNIV_MEM_ALLOC(addr, size) VALGRIND_MAKE_MEM_UNDEFINED(addr, size)
+# define UNIV_MEM_ASSERT_RW(addr, size) do { \
+ const void* _p = (const void*) (ulint) \
+ VALGRIND_CHECK_MEM_IS_DEFINED(addr, size); \
+ if (UNIV_LIKELY_NULL(_p)) \
+ fprintf(stderr, "%s:%d: %p[%u] undefined at %ld\n", \
+ __FILE__, __LINE__, \
+ (const void*) (addr), (unsigned) (size), (long) \
+ (((const char*) _p) - ((const char*) (addr)))); \
+ } while (0)
+# define UNIV_MEM_ASSERT_W(addr, size) do { \
+ const void* _p = (const void*) (ulint) \
+ VALGRIND_CHECK_MEM_IS_ADDRESSABLE(addr, size); \
+ if (UNIV_LIKELY_NULL(_p)) \
+ fprintf(stderr, "%s:%d: %p[%u] unwritable at %ld\n", \
+ __FILE__, __LINE__, \
+ (const void*) (addr), (unsigned) (size), (long) \
+ (((const char*) _p) - ((const char*) (addr)))); \
+ } while (0)
#else
# define UNIV_MEM_VALID(addr, size) do {} while(0)
# define UNIV_MEM_INVALID(addr, size) do {} while(0)
# define UNIV_MEM_FREE(addr, size) do {} while(0)
# define UNIV_MEM_ALLOC(addr, size) do {} while(0)
+# define UNIV_MEM_ASSERT_RW(addr, size) do {} while(0)
+# define UNIV_MEM_ASSERT_W(addr, size) do {} while(0)
#endif
+#define UNIV_MEM_ASSERT_AND_FREE(addr, size) do { \
+ UNIV_MEM_ASSERT_W(addr, size); \
+ UNIV_MEM_FREE(addr, size); \
+} while (0)
+#define UNIV_MEM_ASSERT_AND_ALLOC(addr, size) do { \
+ UNIV_MEM_ASSERT_W(addr, size); \
+ UNIV_MEM_ALLOC(addr, size); \
+} while (0)
#endif
diff --git a/storage/innobase/include/ut0ut.h b/storage/innobase/include/ut0ut.h
index 825c10d5f11..a60ce73c35a 100644
--- a/storage/innobase/include/ut0ut.h
+++ b/storage/innobase/include/ut0ut.h
@@ -263,6 +263,24 @@ ut_copy_file(
FILE* dest, /* in: output file */
FILE* src); /* in: input file to be appended to output */
+/**************************************************************************
+snprintf(). */
+
+#ifdef __WIN__
+int
+ut_snprintf(
+ /* out: number of characters that would
+ have been printed if the size were
+ unlimited, not including the terminating
+ '\0'. */
+ char* str, /* out: string */
+ size_t size, /* in: str size */
+ const char* fmt, /* in: format */
+ ...); /* in: format values */
+#else
+#define ut_snprintf snprintf
+#endif /* __WIN__ */
+
#ifndef UNIV_NONINL
#include "ut0ut.ic"
#endif
diff --git a/storage/innobase/mem/mem0dbg.c b/storage/innobase/mem/mem0dbg.c
index eb77dd01f6d..72452907c3f 100644
--- a/storage/innobase/mem/mem0dbg.c
+++ b/storage/innobase/mem/mem0dbg.c
@@ -223,6 +223,8 @@ mem_init_buf(
{
byte* ptr;
+ UNIV_MEM_ASSERT_W(buf, n);
+
for (ptr = buf; ptr < buf + n; ptr++) {
if (ut_rnd_gen_ibool()) {
@@ -231,6 +233,8 @@ mem_init_buf(
*ptr = 0xBE;
}
}
+
+ UNIV_MEM_INVALID(buf, n);
}
/*******************************************************************
@@ -245,6 +249,8 @@ mem_erase_buf(
{
byte* ptr;
+ UNIV_MEM_ASSERT_W(buf, n);
+
for (ptr = buf; ptr < buf + n; ptr++) {
if (ut_rnd_gen_ibool()) {
*ptr = 0xDE;
@@ -252,6 +258,8 @@ mem_erase_buf(
*ptr = 0xAD;
}
}
+
+ UNIV_MEM_FREE(buf, n);
}
/*******************************************************************
@@ -546,9 +554,7 @@ completed:
}
*error = FALSE;
}
-#endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */
-#ifdef UNIV_DEBUG
/******************************************************************
Prints the contents of a memory heap. */
static
@@ -575,20 +581,6 @@ mem_heap_print(
}
/******************************************************************
-Checks that an object is a memory heap (or a block of it). */
-
-ibool
-mem_heap_check(
-/*===========*/
- /* out: TRUE if ok */
- mem_heap_t* heap) /* in: memory heap */
-{
- ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N);
-
- return(TRUE);
-}
-
-/******************************************************************
Validates the contents of a memory heap. */
ibool
@@ -614,6 +606,22 @@ mem_heap_validate(
return(TRUE);
}
+#endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */
+
+#ifdef UNIV_DEBUG
+/******************************************************************
+Checks that an object is a memory heap (or a block of it). */
+
+ibool
+mem_heap_check(
+/*===========*/
+ /* out: TRUE if ok */
+ mem_heap_t* heap) /* in: memory heap */
+{
+ ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N);
+
+ return(TRUE);
+}
#endif /* UNIV_DEBUG */
#ifdef UNIV_MEM_DEBUG
diff --git a/storage/innobase/mem/mem0mem.c b/storage/innobase/mem/mem0mem.c
index d89a3a55d88..f4fd178a39c 100644
--- a/storage/innobase/mem/mem0mem.c
+++ b/storage/innobase/mem/mem0mem.c
@@ -512,9 +512,9 @@ mem_heap_block_free(
of hex 0xDE and 0xAD. */
mem_erase_buf((byte*)block, len);
-
-#endif
- UNIV_MEM_FREE(block, len);
+#else /* UNIV_MEM_DEBUG */
+ UNIV_MEM_ASSERT_AND_FREE(block, len);
+#endif /* UNIV_MEM_DEBUG */
if (init_block) {
/* Do not have to free: do nothing */
diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c
index b8d201e3da2..b3cf20b78b8 100644
--- a/storage/innobase/row/row0mysql.c
+++ b/storage/innobase/row/row0mysql.c
@@ -1728,10 +1728,11 @@ row_mysql_unlock_data_dictionary(
}
/*************************************************************************
-Does a table creation operation for MySQL. If the name of the table
-to be created is equal with one of the predefined magic table names,
-then this also starts printing the corresponding monitor output by
-the master thread. */
+Drops a table for MySQL. If the name of the table ends in
+one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor",
+"innodb_table_monitor", then this will also start the printing of monitor
+output by the master thread. If the table name ends in "innodb_mem_validate",
+InnoDB will try to invoke mem_validate(). */
int
row_create_table_for_mysql(
@@ -1756,13 +1757,11 @@ row_create_table_for_mysql(
ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
if (srv_created_new_raw) {
- fputs("InnoDB: A new raw disk partition was initialized or\n"
- "InnoDB: innodb_force_recovery is on: we do not allow\n"
- "InnoDB: database modifications by the user. Shut down\n"
- "InnoDB: mysqld and edit my.cnf so that newraw"
- " is replaced\n"
- "InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
+ fputs("InnoDB: A new raw disk partition was initialized:\n"
+ "InnoDB: we do not allow database modifications"
+ " by the user.\n"
+ "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
+ " is replaced with raw.\n", stderr);
dict_mem_table_free(table);
trx_commit_for_mysql(trx);
@@ -2703,13 +2702,11 @@ row_truncate_table_for_mysql(
ut_ad(table);
if (srv_created_new_raw) {
- fputs("InnoDB: A new raw disk partition was initialized or\n"
- "InnoDB: innodb_force_recovery is on: we do not allow\n"
- "InnoDB: database modifications by the user. Shut down\n"
- "InnoDB: mysqld and edit my.cnf so that newraw"
- " is replaced\n"
- "InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
+ fputs("InnoDB: A new raw disk partition was initialized:\n"
+ "InnoDB: we do not allow database modifications"
+ " by the user.\n"
+ "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
+ " is replaced with raw.\n", stderr);
return(DB_ERROR);
}
@@ -2898,7 +2895,9 @@ next_rec:
/* MySQL calls ha_innobase::reset_auto_increment() which does
the same thing. */
+ dict_table_autoinc_lock(table);
dict_table_autoinc_initialize(table, 0);
+ dict_table_autoinc_unlock(table);
dict_update_statistics(table);
trx_commit_for_mysql(trx);
@@ -2916,9 +2915,10 @@ funct_exit:
#endif /* !UNIV_HOTBACKUP */
/*************************************************************************
-Drops a table for MySQL. If the name of the table to be dropped is equal
-with one of the predefined magic table names, then this also stops printing
-the corresponding monitor output by the master thread. */
+Drops a table for MySQL. If the name of the dropped table ends in
+one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor",
+"innodb_table_monitor", then this will also stop the printing of monitor
+output by the master thread. */
int
row_drop_table_for_mysql(
@@ -2934,21 +2934,17 @@ row_drop_table_for_mysql(
ulint err;
const char* table_name;
ulint namelen;
- char* dir_path_of_temp_table = NULL;
- ibool success;
ibool locked_dictionary = FALSE;
pars_info_t* info = NULL;
ut_a(name != NULL);
if (srv_created_new_raw) {
- fputs("InnoDB: A new raw disk partition was initialized or\n"
- "InnoDB: innodb_force_recovery is on: we do not allow\n"
- "InnoDB: database modifications by the user. Shut down\n"
- "InnoDB: mysqld and edit my.cnf so that newraw"
- " is replaced\n"
- "InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
+ fputs("InnoDB: A new raw disk partition was initialized:\n"
+ "InnoDB: we do not allow database modifications"
+ " by the user.\n"
+ "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
+ " is replaced with raw.\n", stderr);
return(DB_ERROR);
}
@@ -3232,14 +3228,20 @@ check_next_foreign:
} else {
ibool is_path;
const char* name_or_path;
+ mem_heap_t* heap;
+ heap = mem_heap_create(200);
+
+ /* Clone the name, in case it has been allocated
+ from table->heap, which will be freed by
+ dict_table_remove_from_cache(table) below. */
+ name = mem_heap_strdup(heap, name);
space_id = table->space;
if (table->dir_path_of_temp_table != NULL) {
- dir_path_of_temp_table = mem_strdup(
- table->dir_path_of_temp_table);
is_path = TRUE;
- name_or_path = dir_path_of_temp_table;
+ name_or_path = mem_heap_strdup(
+ heap, table->dir_path_of_temp_table);
} else {
is_path = FALSE;
name_or_path = name;
@@ -3272,13 +3274,7 @@ check_next_foreign:
"InnoDB: of table ");
ut_print_name(stderr, trx, TRUE, name);
fprintf(stderr, ".\n");
-
- goto funct_exit;
- }
-
- success = fil_delete_tablespace(space_id);
-
- if (!success) {
+ } else if (!fil_delete_tablespace(space_id)) {
fprintf(stderr,
"InnoDB: We removed now the InnoDB"
" internal data dictionary entry\n"
@@ -3296,6 +3292,8 @@ check_next_foreign:
err = DB_ERROR;
}
}
+
+ mem_heap_free(heap);
}
funct_exit:
@@ -3305,10 +3303,6 @@ funct_exit:
row_mysql_unlock_data_dictionary(trx);
}
- if (dir_path_of_temp_table) {
- mem_free(dir_path_of_temp_table);
- }
-
trx->op_info = "";
#ifndef UNIV_HOTBACKUP
diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c
index 9cb4e0f6f20..29bded114e0 100644
--- a/storage/innobase/row/row0sel.c
+++ b/storage/innobase/row/row0sel.c
@@ -531,12 +531,13 @@ row_sel_build_prev_vers(
/*====================*/
/* out: DB_SUCCESS or error code */
read_view_t* read_view, /* in: read view */
- plan_t* plan, /* in: plan node for table */
+ dict_index_t* index, /* in: plan node for table */
rec_t* rec, /* in: record in a clustered index */
ulint** offsets, /* in/out: offsets returned by
rec_get_offsets(rec, plan->index) */
mem_heap_t** offset_heap, /* in/out: memory heap from which
the offsets are allocated */
+ mem_heap_t** old_vers_heap, /* out: old version heap to use */
rec_t** old_vers, /* out: old version, or NULL if the
record does not exist in the view:
i.e., it was freshly inserted
@@ -545,15 +546,15 @@ row_sel_build_prev_vers(
{
ulint err;
- if (plan->old_vers_heap) {
- mem_heap_empty(plan->old_vers_heap);
+ if (*old_vers_heap) {
+ mem_heap_empty(*old_vers_heap);
} else {
- plan->old_vers_heap = mem_heap_create(512);
+ *old_vers_heap = mem_heap_create(512);
}
err = row_vers_build_for_consistent_read(
- rec, mtr, plan->index, offsets, read_view, offset_heap,
- plan->old_vers_heap, old_vers);
+ rec, mtr, index, offsets, read_view, offset_heap,
+ *old_vers_heap, old_vers);
return(err);
}
@@ -765,9 +766,11 @@ row_sel_get_clust_rec(
if (!lock_clust_rec_cons_read_sees(clust_rec, index, offsets,
node->read_view)) {
- err = row_sel_build_prev_vers(node->read_view, plan,
- clust_rec, &offsets,
- &heap, &old_vers, mtr);
+ err = row_sel_build_prev_vers(
+ node->read_view, index, clust_rec,
+ &offsets, &heap, &plan->old_vers_heap,
+ &old_vers, mtr);
+
if (err != DB_SUCCESS) {
goto err_exit;
@@ -1490,10 +1493,11 @@ skip_lock:
if (!lock_clust_rec_cons_read_sees(rec, index, offsets,
node->read_view)) {
- err = row_sel_build_prev_vers(node->read_view,
- plan, rec,
- &offsets, &heap,
- &old_vers, &mtr);
+ err = row_sel_build_prev_vers(
+ node->read_view, index, rec,
+ &offsets, &heap, &plan->old_vers_heap,
+ &old_vers, &mtr);
+
if (err != DB_SUCCESS) {
goto lock_wait_or_error;
@@ -3999,6 +4003,7 @@ no_gap_lock:
mutex_enter(&kernel_mutex);
if (trx->was_chosen_as_deadlock_victim) {
mutex_exit(&kernel_mutex);
+ err = DB_DEADLOCK;
goto lock_wait_or_error;
}
@@ -4521,7 +4526,8 @@ row_search_check_if_query_cache_permitted(
}
/***********************************************************************
-Read the AUTOINC column from the current row. */
+Read the AUTOINC column from the current row. If the value is less than
+0 and the type is not unsigned then we reset the value to 0. */
static
ib_longlong
row_search_autoinc_read_column(
@@ -4533,10 +4539,10 @@ row_search_autoinc_read_column(
ibool unsigned_type) /* in: signed or unsigned flag */
{
ulint len;
- byte* ptr;
const byte* data;
ib_longlong value;
mem_heap_t* heap = NULL;
+ /* Our requirement is that dest should be word aligned. */
byte dest[sizeof(value)];
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
@@ -4555,44 +4561,30 @@ row_search_autoinc_read_column(
ut_a(len != UNIV_SQL_NULL);
ut_a(len <= sizeof value);
-#ifdef WORDS_BIGENDIAN
- /* Copy integer data and restore sign bit */
-
- memcpy((ptr = dest), data, len);
+ mach_read_int_type(dest, data, len, unsigned_type);
- if (!unsigned_type) {
- dest[0] ^= 128;
- }
-#else
- /* Convert integer data from Innobase to a little-endian format,
- sign bit restored to normal */
-
- for (ptr = dest + len; ptr != dest; ++data) {
- --ptr;
- *ptr = *data;
- }
-
- if (!unsigned_type) {
- dest[len - 1] ^= 128;
- }
-#endif
-
- /* The assumption here is that the AUTOINC value can't be negative.*/
+ /* The assumption here is that the AUTOINC value can't be negative
+ and that dest is word aligned. */
switch (len) {
case 8:
- value = *(ib_longlong*) ptr;
+ value = *(ib_longlong*) dest;
break;
case 4:
- value = *(ib_uint32_t*) ptr;
+ value = *(ib_uint32_t*) dest;
+ break;
+
+ case 3:
+ value = *(ib_uint32_t*) dest;
+ value &= 0xFFFFFF;
break;
case 2:
- value = *(uint16 *) ptr;
+ value = *(uint16 *) dest;
break;
case 1:
- value = *ptr;
+ value = *dest;
break;
default:
@@ -4603,7 +4595,9 @@ row_search_autoinc_read_column(
mem_heap_free(heap);
}
- ut_a(value >= 0);
+ if (!unsigned_type && value < 0) {
+ value = 0;
+ }
return(value);
}
diff --git a/storage/innobase/sync/sync0rw.c b/storage/innobase/sync/sync0rw.c
index 4db780c8b3f..8dbc69816e9 100644
--- a/storage/innobase/sync/sync0rw.c
+++ b/storage/innobase/sync/sync0rw.c
@@ -174,9 +174,7 @@ rw_lock_free(
/*=========*/
rw_lock_t* lock) /* in: rw-lock */
{
-#ifdef UNIV_DEBUG
- ut_a(rw_lock_validate(lock));
-#endif /* UNIV_DEBUG */
+ ut_ad(rw_lock_validate(lock));
ut_a(rw_lock_get_writer(lock) == RW_LOCK_NOT_LOCKED);
ut_a(rw_lock_get_waiters(lock) == 0);
ut_a(rw_lock_get_reader_count(lock) == 0);
@@ -199,6 +197,7 @@ rw_lock_free(
mutex_exit(&rw_lock_list_mutex);
}
+#ifdef UNIV_DEBUG
/**********************************************************************
Checks that the rw-lock has been initialized and that there are no
simultaneous shared and exclusive locks. */
@@ -226,6 +225,7 @@ rw_lock_validate(
return(TRUE);
}
+#endif /* UNIV_DEBUG */
/**********************************************************************
Lock an rw-lock in shared mode for the current thread. If the rw-lock is
diff --git a/storage/innobase/sync/sync0sync.c b/storage/innobase/sync/sync0sync.c
index bf3f4d1ff20..39872f72204 100644
--- a/storage/innobase/sync/sync0sync.c
+++ b/storage/innobase/sync/sync0sync.c
@@ -830,7 +830,7 @@ sync_thread_levels_g(
mutex = slot->latch;
fprintf(stderr,
- "InnoDB error: sync levels should be"
+ "InnoDB: sync levels should be"
" > %lu but a level is %lu\n",
(ulong) limit, (ulong) slot->level);
diff --git a/storage/innobase/ut/ut0ut.c b/storage/innobase/ut/ut0ut.c
index 389063ad821..1ca278cd633 100644
--- a/storage/innobase/ut/ut0ut.c
+++ b/storage/innobase/ut/ut0ut.c
@@ -18,6 +18,7 @@ Created 5/11/1994 Heikki Tuuri
#include "ut0sort.h"
#include "trx0trx.h"
+#include "ha_prototypes.h"
ibool ut_always_false = FALSE;
@@ -70,22 +71,6 @@ ut_gettimeofday(
#define ut_gettimeofday gettimeofday
#endif
-#ifndef UNIV_HOTBACKUP
-/*********************************************************************
-Display an SQL identifier.
-This definition must match the one in sql/ha_innodb.cc! */
-extern
-void
-innobase_print_identifier(
-/*======================*/
- FILE* f, /* in: output stream */
- trx_t* trx, /* in: transaction */
- ibool table_id,/* in: TRUE=print a table name,
- FALSE=print other identifier */
- const char* name, /* in: name to print */
- ulint namelen);/* in: length of name */
-#endif /* !UNIV_HOTBACKUP */
-
/************************************************************
Gets the high 32 bits in a ulint. That is makes a shift >> 32,
but since there seem to be compiler bugs in both gcc and Visual C++,
@@ -360,6 +345,8 @@ ut_print_buf(
const byte* data;
ulint i;
+ UNIV_MEM_ASSERT_RW(buf, len);
+
fprintf(file, " len %lu; hex ", len);
for (data = (const byte*)buf, i = 0; i < len; i++) {
@@ -474,17 +461,20 @@ ut_print_namel(
#ifdef UNIV_HOTBACKUP
fwrite(name, 1, namelen, f);
#else
- char* slash = memchr(name, '/', namelen);
+ if (table_id) {
+ char* slash = memchr(name, '/', namelen);
+ if (!slash) {
- if (UNIV_LIKELY_NULL(slash)) {
- /* Print the database name and table name separately. */
- ut_ad(table_id);
+ goto no_db_name;
+ }
+ /* Print the database name and table name separately. */
innobase_print_identifier(f, trx, TRUE, name, slash - name);
putc('.', f);
innobase_print_identifier(f, trx, TRUE, slash + 1,
namelen - (slash - name) - 1);
} else {
+no_db_name:
innobase_print_identifier(f, trx, table_id, name, namelen);
}
#endif
@@ -515,3 +505,44 @@ ut_copy_file(
}
} while (len > 0);
}
+
+/**************************************************************************
+snprintf(). */
+
+#ifdef __WIN__
+#include <stdarg.h>
+int
+ut_snprintf(
+ /* out: number of characters that would
+ have been printed if the size were
+ unlimited, not including the terminating
+ '\0'. */
+ char* str, /* out: string */
+ size_t size, /* in: str size */
+ const char* fmt, /* in: format */
+ ...) /* in: format values */
+{
+ int res;
+ va_list ap1;
+ va_list ap2;
+
+ va_start(ap1, fmt);
+ va_start(ap2, fmt);
+
+ res = _vscprintf(fmt, ap1);
+ ut_a(res != -1);
+
+ if (size > 0) {
+ _vsnprintf(str, size, fmt, ap2);
+
+ if ((size_t) res >= size) {
+ str[size - 1] = '\0';
+ }
+ }
+
+ va_end(ap1);
+ va_end(ap2);
+
+ return(res);
+}
+#endif /* __WIN__ */
diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c
index 15f4e1e1d34..5e7a955793f 100644
--- a/storage/myisam/ft_boolean_search.c
+++ b/storage/myisam/ft_boolean_search.c
@@ -576,8 +576,8 @@ FT_INFO * ft_init_boolean_search(MI_INFO *info, uint keynr, uchar *query,
ftb->list=(FTB_WORD **)alloc_root(&ftb->mem_root,
sizeof(FTB_WORD *)*ftb->queue.elements);
memcpy(ftb->list, ftb->queue.root+1, sizeof(FTB_WORD *)*ftb->queue.elements);
- qsort2(ftb->list, ftb->queue.elements, sizeof(FTB_WORD *),
- (qsort2_cmp)FTB_WORD_cmp_list, ftb->charset);
+ my_qsort2(ftb->list, ftb->queue.elements, sizeof(FTB_WORD *),
+ (qsort2_cmp)FTB_WORD_cmp_list, ftb->charset);
if (ftb->queue.elements<2) ftb->with_scan &= ~FTB_FLAG_TRUNC;
ftb->state=READY;
return ftb;
diff --git a/storage/myisam/ft_nlq_search.c b/storage/myisam/ft_nlq_search.c
index 282fa6751d8..8df8c2da4eb 100644
--- a/storage/myisam/ft_nlq_search.c
+++ b/storage/myisam/ft_nlq_search.c
@@ -294,7 +294,8 @@ FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, uchar *query,
&dptr, left_root_right);
if (flags & FT_SORTED)
- qsort2(dlist->doc, dlist->ndocs, sizeof(FT_DOC), (qsort2_cmp)&FT_DOC_cmp, 0);
+ my_qsort2(dlist->doc, dlist->ndocs, sizeof(FT_DOC), (qsort2_cmp)&FT_DOC_cmp,
+ 0);
err:
delete_tree(&aio.dtree);
diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
index bc6e5706c21..0b69f9d135f 100644
--- a/storage/myisam/ha_myisam.cc
+++ b/storage/myisam/ha_myisam.cc
@@ -119,6 +119,10 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
definition for further use in mi_create or for a check for underlying
table conformance in merge engine.
+ The caller needs to free *recinfo_out after use. Since *recinfo_out
+ and *keydef_out are allocated with a my_multi_malloc, *keydef_out
+ is freed automatically when *recinfo_out is freed.
+
RETURN VALUE
0 OK
!0 error code
@@ -149,7 +153,7 @@ int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
pos= table_arg->key_info;
for (i= 0; i < share->keys; i++, pos++)
{
- keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
+ keydef[i].flag= ((uint16) pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
keydef[i].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ?
(pos->flags & HA_SPATIAL ? HA_KEY_ALG_RTREE : HA_KEY_ALG_BTREE) :
pos->algorithm;
@@ -1403,10 +1407,8 @@ int ha_myisam::enable_indexes(uint mode)
might have been set by the first repair. They can still be seen
with SHOW WARNINGS then.
*/
-#ifndef EMBEDDED_LIBRARY
if (! error)
thd->clear_error();
-#endif /* EMBEDDED_LIBRARY */
}
info(HA_STATUS_CONST);
thd->proc_info=save_proc_info;
@@ -1461,7 +1463,7 @@ void ha_myisam::start_bulk_insert(ha_rows rows)
DBUG_ENTER("ha_myisam::start_bulk_insert");
THD *thd= current_thd;
ulong size= min(thd->variables.read_buff_size,
- table->s->avg_row_length*rows);
+ (ulong) (table->s->avg_row_length*rows));
DBUG_PRINT("info",("start_bulk_insert: rows %lu size %lu",
(ulong) rows, size));
@@ -1649,9 +1651,13 @@ int ha_myisam::index_next_same(uchar *buf,
const uchar *key __attribute__((unused)),
uint length __attribute__((unused)))
{
+ int error;
DBUG_ASSERT(inited==INDEX);
ha_statistic_increment(&SSV::ha_read_next_count);
- int error=mi_rnext_same(file,buf);
+ do
+ {
+ error= mi_rnext_same(file,buf);
+ } while (error == HA_ERR_RECORD_DELETED);
table->status=error ? STATUS_NOT_FOUND: 0;
return error;
}
diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h
index e8594fc9039..ca44ae9ad87 100644
--- a/storage/myisam/ha_myisam.h
+++ b/storage/myisam/ha_myisam.h
@@ -140,4 +140,8 @@ class ha_myisam: public handler
*engine_callback,
ulonglong *engine_data);
#endif
+ MI_INFO *file_ptr(void)
+ {
+ return file;
+ }
};
diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c
index fe6b716877c..3cc82b832c9 100644
--- a/storage/myisam/mi_check.c
+++ b/storage/myisam/mi_check.c
@@ -455,7 +455,7 @@ int chk_key(MI_CHECK *param, register MI_INFO *info)
if ((!(param->testflag & T_SILENT)))
printf ("- check data record references index: %d\n",key+1);
- if (keyinfo->flag & HA_FULLTEXT)
+ if (keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL))
full_text_keys++;
if (share->state.key_root[key] == HA_OFFSET_ERROR &&
(info->state->records == 0 || keyinfo->flag & HA_FULLTEXT))
@@ -941,7 +941,7 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend)
ha_rows records,del_blocks;
my_off_t used,empty,pos,splits,start_recpos,
del_length,link_used,start_block;
- uchar *record,*to;
+ uchar *record= 0, *to;
char llbuff[22],llbuff2[22],llbuff3[22];
ha_checksum intern_record_checksum;
ha_checksum key_checksum[MI_MAX_POSSIBLE_KEY];
@@ -958,7 +958,7 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend)
puts("- check record links");
}
- if (!(record= (uchar*) my_malloc(info->s->base.pack_reclength,MYF(0))))
+ if (!mi_alloc_rec_buff(info, -1, &record))
{
mi_check_print_error(param,"Not enough memory for record");
DBUG_RETURN(-1);
@@ -1365,17 +1365,150 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend)
printf("Lost space: %12s Linkdata: %10s\n",
llstr(empty,llbuff),llstr(link_used,llbuff2));
}
- my_free((uchar*) record,MYF(0));
+ my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
DBUG_RETURN (error);
err:
mi_check_print_error(param,"got error: %d when reading datafile at record: %s",my_errno, llstr(records,llbuff));
err2:
- my_free((uchar*) record,MYF(0));
+ my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
param->testflag|=T_RETRY_WITHOUT_QUICK;
DBUG_RETURN(1);
} /* chk_data_link */
+/**
+ @brief Drop all indexes
+
+ @param[in] param check parameters
+ @param[in] info MI_INFO handle
+ @param[in] force if to force drop all indexes
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error
+
+ @note
+ Once allocated, index blocks remain part of the key file forever.
+ When indexes are disabled, no block is freed. When enabling indexes,
+ no block is freed either. The new indexes are create from new
+ blocks. (Bug #4692)
+
+ Before recreating formerly disabled indexes, the unused blocks
+ must be freed. There are two options to do this:
+ - Follow the tree of disabled indexes, add all blocks to the
+ deleted blocks chain. Would require a lot of random I/O.
+ - Drop all blocks by clearing all index root pointers and all
+ delete chain pointers and resetting key_file_length to the end
+ of the index file header. This requires to recreate all indexes,
+ even those that may still be intact.
+ The second method is probably faster in most cases.
+
+ When disabling indexes, MySQL disables either all indexes or all
+ non-unique indexes. When MySQL [re-]enables disabled indexes
+ (T_CREATE_MISSING_KEYS), then we either have "lost" blocks in the
+ index file, or there are no non-unique indexes. In the latter case,
+ mi_repair*() would not be called as there would be no disabled
+ indexes.
+
+ If there would be more unique indexes than disabled (non-unique)
+ indexes, we could do the first method. But this is not implemented
+ yet. By now we drop and recreate all indexes when repair is called.
+
+ However, there is an exception. Sometimes MySQL disables non-unique
+ indexes when the table is empty (e.g. when copying a table in
+ mysql_alter_table()). When enabling the non-unique indexes, they
+ are still empty. So there is no index block that can be lost. This
+ optimization is implemented in this function.
+
+ Note that in normal repair (T_CREATE_MISSING_KEYS not set) we
+ recreate all enabled indexes unconditonally. We do not change the
+ key_map. Otherwise we invert the key map temporarily (outside of
+ this function) and recreate the then "seemingly" enabled indexes.
+ When we cannot use the optimization, and drop all indexes, we
+ pretend that all indexes were disabled. By the inversion, we will
+ then recrate all indexes.
+*/
+
+static int mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, my_bool force)
+{
+ MYISAM_SHARE *share= info->s;
+ MI_STATE_INFO *state= &share->state;
+ uint i;
+ int error;
+ DBUG_ENTER("mi_drop_all_indexes");
+
+ /*
+ If any of the disabled indexes has a key block assigned, we must
+ drop and recreate all indexes to avoid losing index blocks.
+
+ If we want to recreate disabled indexes only _and_ all of these
+ indexes are empty, we don't need to recreate the existing indexes.
+ */
+ if (!force && (param->testflag & T_CREATE_MISSING_KEYS))
+ {
+ DBUG_PRINT("repair", ("creating missing indexes"));
+ for (i= 0; i < share->base.keys; i++)
+ {
+ DBUG_PRINT("repair", ("index #: %u key_root: 0x%lx active: %d",
+ i, (long) state->key_root[i],
+ mi_is_key_active(state->key_map, i)));
+ if ((state->key_root[i] != HA_OFFSET_ERROR) &&
+ !mi_is_key_active(state->key_map, i))
+ {
+ /*
+ This index has at least one key block and it is disabled.
+ We would lose its block(s) if would just recreate it.
+ So we need to drop and recreate all indexes.
+ */
+ DBUG_PRINT("repair", ("nonempty and disabled: recreate all"));
+ break;
+ }
+ }
+ if (i >= share->base.keys)
+ {
+ /*
+ All of the disabled indexes are empty. We can just recreate them.
+ Flush dirty blocks of this index file from key cache and remove
+ all blocks of this index file from key cache.
+ */
+ DBUG_PRINT("repair", ("all disabled are empty: create missing"));
+ error= flush_key_blocks(share->key_cache, share->kfile,
+ FLUSH_FORCE_WRITE);
+ goto end;
+ }
+ /*
+ We do now drop all indexes and declare them disabled. With the
+ T_CREATE_MISSING_KEYS flag, mi_repair*() will recreate all
+ disabled indexes and enable them.
+ */
+ mi_clear_all_keys_active(state->key_map);
+ DBUG_PRINT("repair", ("declared all indexes disabled"));
+ }
+
+ /* Remove all key blocks of this index file from key cache. */
+ if ((error= flush_key_blocks(share->key_cache, share->kfile,
+ FLUSH_IGNORE_CHANGED)))
+ goto end; /* purecov: inspected */
+
+ /* Clear index root block pointers. */
+ for (i= 0; i < share->base.keys; i++)
+ state->key_root[i]= HA_OFFSET_ERROR;
+
+ /* Clear the delete chains. */
+ for (i= 0; i < state->header.max_block_size_index; i++)
+ state->key_del[i]= HA_OFFSET_ERROR;
+
+ /* Reset index file length to end of index file header. */
+ info->state->key_file_length= share->base.keystart;
+
+ DBUG_PRINT("repair", ("dropped all indexes"));
+ /* error= 0; set by last (error= flush_key_bocks()). */
+
+ end:
+ DBUG_RETURN(error);
+}
+
+
/* Recover old table by reading each record and writing all keys */
/* Save new datafile-name in temp_filename */
@@ -1383,7 +1516,6 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info,
char * name, int rep_quick)
{
int error,got_error;
- uint i;
ha_rows start_records,new_header_length;
my_off_t del;
File new_file;
@@ -1429,8 +1561,7 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info,
MYF(MY_WME | MY_WAIT_IF_FULL)))
goto err;
info->opt_flag|=WRITE_CACHE_USED;
- if (!(sort_param.record=(uchar*) my_malloc((uint) share->base.pack_reclength,
- MYF(0))) ||
+ if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
!mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
{
mi_check_print_error(param, "Not enough memory for extra record");
@@ -1487,25 +1618,10 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info,
info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
- /*
- Clear all keys. Note that all key blocks allocated until now remain
- "dead" parts of the key file. (Bug #4692)
- */
- for (i=0 ; i < info->s->base.keys ; i++)
- share->state.key_root[i]= HA_OFFSET_ERROR;
-
- /* Drop the delete chain. */
- for (i=0 ; i < share->state.header.max_block_size_index ; i++)
- share->state.key_del[i]= HA_OFFSET_ERROR;
-
- /*
- If requested, activate (enable) all keys in key_map. In this case,
- all indexes will be (re-)built.
- */
+ /* This function always recreates all enabled indexes. */
if (param->testflag & T_CREATE_MISSING_KEYS)
mi_set_all_keys_active(share->state.key_map, share->base.keys);
-
- info->state->key_file_length=share->base.keystart;
+ mi_drop_all_indexes(param, info, TRUE);
lock_memory(param); /* Everything is alloced */
@@ -1637,7 +1753,8 @@ err:
}
my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff),
MYF(MY_ALLOW_ZERO_PTR));
- my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mi_get_rec_buff_ptr(info, sort_param.record),
+ MYF(MY_ALLOW_ZERO_PTR));
my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
VOID(end_io_cache(&param->read_cache));
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
@@ -2106,8 +2223,9 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
ulong *rec_per_key_part;
char llbuff[22];
SORT_INFO sort_info;
- ulonglong key_map=share->state.key_map;
+ ulonglong key_map;
DBUG_ENTER("mi_repair_by_sort");
+ LINT_INIT(key_map);
start_records=info->state->records;
got_error=1;
@@ -2143,8 +2261,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
info->opt_flag|=WRITE_CACHE_USED;
info->rec_cache.file=info->dfile; /* for sort_delete_record */
- if (!(sort_param.record=(uchar*) my_malloc((uint) share->base.pack_reclength,
- MYF(0))) ||
+ if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
!mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
{
mi_check_print_error(param, "Not enough memory for extra record");
@@ -2180,25 +2297,14 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
}
info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
- if (!(param->testflag & T_CREATE_MISSING_KEYS))
- {
- /*
- Flush key cache for this file if we are calling this outside
- myisamchk
- */
- flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED);
- /* Clear the pointers to the given rows */
- for (i=0 ; i < share->base.keys ; i++)
- share->state.key_root[i]= HA_OFFSET_ERROR;
- for (i=0 ; i < share->state.header.max_block_size_index ; i++)
- share->state.key_del[i]= HA_OFFSET_ERROR;
- info->state->key_file_length=share->base.keystart;
- }
- else
+
+ /* Optionally drop indexes and optionally modify the key_map. */
+ mi_drop_all_indexes(param, info, FALSE);
+ key_map= share->state.key_map;
+ if (param->testflag & T_CREATE_MISSING_KEYS)
{
- if (flush_key_blocks(share->key_cache,share->kfile, FLUSH_FORCE_WRITE))
- goto err;
- key_map= ~key_map; /* Create the missing keys */
+ /* Invert the copied key_map to recreate all disabled indexes. */
+ key_map= ~key_map;
}
sort_info.info=info;
@@ -2242,6 +2348,10 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
sort_param.read_cache=param->read_cache;
sort_param.keyinfo=share->keyinfo+sort_param.key;
sort_param.seg=sort_param.keyinfo->seg;
+ /*
+ Skip this index if it is marked disabled in the copied
+ (and possibly inverted) key_map.
+ */
if (! mi_is_key_active(key_map, sort_param.key))
{
/* Remember old statistics for key */
@@ -2249,6 +2359,8 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
(char*) (share->state.rec_per_key_part +
(uint) (rec_per_key_part - param->rec_per_key_part)),
sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part));
+ DBUG_PRINT("repair", ("skipping seemingly disabled index #: %u",
+ sort_param.key));
continue;
}
@@ -2329,8 +2441,11 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
if (param->testflag & T_STATISTICS)
update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
- sort_param.notnull: NULL,(ulonglong) info->state->records);
+ sort_param.notnull: NULL,
+ (ulonglong) info->state->records);
+ /* Enable this index in the permanent (not the copied) key_map. */
mi_set_key_active(share->state.key_map, sort_param.key);
+ DBUG_PRINT("repair", ("set enabled index #: %u", sort_param.key));
if (sort_param.fix_datafile)
{
@@ -2455,7 +2570,8 @@ err:
my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff),
MYF(MY_ALLOW_ZERO_PTR));
- my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mi_get_rec_buff_ptr(info, sort_param.record),
+ MYF(MY_ALLOW_ZERO_PTR));
my_free((uchar*) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
my_free((uchar*) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
@@ -2531,9 +2647,11 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
IO_CACHE new_data_cache; /* For non-quick repair. */
IO_CACHE_SHARE io_share;
SORT_INFO sort_info;
- ulonglong key_map=share->state.key_map;
+ ulonglong key_map;
pthread_attr_t thr_attr;
+ ulong max_pack_reclength;
DBUG_ENTER("mi_repair_parallel");
+ LINT_INIT(key_map);
start_records=info->state->records;
got_error=1;
@@ -2635,25 +2753,14 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
}
info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
- if (!(param->testflag & T_CREATE_MISSING_KEYS))
- {
- /*
- Flush key cache for this file if we are calling this outside
- myisamchk
- */
- flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED);
- /* Clear the pointers to the given rows */
- for (i=0 ; i < share->base.keys ; i++)
- share->state.key_root[i]= HA_OFFSET_ERROR;
- for (i=0 ; i < share->state.header.max_block_size_index ; i++)
- share->state.key_del[i]= HA_OFFSET_ERROR;
- info->state->key_file_length=share->base.keystart;
- }
- else
+
+ /* Optionally drop indexes and optionally modify the key_map. */
+ mi_drop_all_indexes(param, info, FALSE);
+ key_map= share->state.key_map;
+ if (param->testflag & T_CREATE_MISSING_KEYS)
{
- if (flush_key_blocks(share->key_cache,share->kfile, FLUSH_FORCE_WRITE))
- goto err;
- key_map= ~key_map; /* Create the missing keys */
+ /* Invert the copied key_map to recreate all disabled indexes. */
+ key_map= ~key_map;
}
sort_info.info=info;
@@ -2689,10 +2796,13 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
del=info->state->del;
param->glob_crc=0;
-
+ /* for compressed tables */
+ max_pack_reclength= share->base.pack_reclength;
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ set_if_bigger(max_pack_reclength, share->max_pack_length);
if (!(sort_param=(MI_SORT_PARAM *)
my_malloc((uint) share->base.keys *
- (sizeof(MI_SORT_PARAM) + share->base.pack_reclength),
+ (sizeof(MI_SORT_PARAM) + max_pack_reclength),
MYF(MY_ZEROFILL))))
{
mi_check_print_error(param,"Not enough memory for key!");
@@ -2709,6 +2819,10 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
sort_param[i].key=key;
sort_param[i].keyinfo=share->keyinfo+key;
sort_param[i].seg=sort_param[i].keyinfo->seg;
+ /*
+ Skip this index if it is marked disabled in the copied
+ (and possibly inverted) key_map.
+ */
if (! mi_is_key_active(key_map, key))
{
/* Remember old statistics for key */
@@ -2744,7 +2858,7 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length;
sort_param[i].record= (((uchar *)(sort_param+share->base.keys))+
- (share->base.pack_reclength * i));
+ (max_pack_reclength * i));
if (!mi_alloc_rec_buff(info, -1, &sort_param[i].rec_buff))
{
mi_check_print_error(param,"Not enough memory!");
@@ -4359,7 +4473,7 @@ err:
void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
my_bool repair_only)
{
- uchar *record;
+ uchar *record= 0;
DBUG_ENTER("update_auto_increment_key");
if (!info->s->base.auto_key ||
@@ -4378,8 +4492,7 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
We have to use an allocated buffer instead of info->rec_buff as
_mi_put_key_in_record() may use info->rec_buff
*/
- if (!(record= (uchar*) my_malloc((uint) info->s->base.pack_reclength,
- MYF(0))))
+ if (!mi_alloc_rec_buff(info, -1, &record))
{
mi_check_print_error(param,"Not enough memory for extra record");
DBUG_VOID_RETURN;
@@ -4391,7 +4504,7 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
if (my_errno != HA_ERR_END_OF_FILE)
{
mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
- my_free((char*) record, MYF(0));
+ my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
mi_check_print_error(param,"%d when reading last record",my_errno);
DBUG_VOID_RETURN;
}
@@ -4406,7 +4519,7 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
set_if_bigger(info->s->state.auto_increment, param->auto_increment_value);
}
mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
- my_free((char*) record, MYF(0));
+ my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
update_state_info(param, info, UPDATE_AUTO_INC);
DBUG_VOID_RETURN;
}
diff --git a/storage/myisam/mi_dynrec.c b/storage/myisam/mi_dynrec.c
index cdd70abe9ad..9940cf62204 100644
--- a/storage/myisam/mi_dynrec.c
+++ b/storage/myisam/mi_dynrec.c
@@ -326,6 +326,29 @@ static int write_dynamic_record(MI_INFO *info, const uchar *record,
DBUG_ENTER("write_dynamic_record");
flag=0;
+
+ /*
+ Check if we have enough room for the new record.
+ First we do simplified check to make usual case faster.
+ Then we do more precise check for the space left.
+ Though it still is not absolutely precise, as
+ we always use MI_MAX_DYN_BLOCK_HEADER while it can be
+ less in the most of the cases.
+ */
+
+ if (unlikely(info->s->base.max_data_file_length -
+ info->state->data_file_length <
+ reclength + MI_MAX_DYN_BLOCK_HEADER))
+ {
+ if (info->s->base.max_data_file_length - info->state->data_file_length +
+ info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
+ reclength + MI_MAX_DYN_BLOCK_HEADER)
+ {
+ my_errno=HA_ERR_RECORD_FILE_FULL;
+ DBUG_RETURN(1);
+ }
+ }
+
do
{
if (_mi_find_writepos(info,reclength,&filepos,&length))
@@ -762,6 +785,51 @@ static int update_dynamic_record(MI_INFO *info, my_off_t filepos, uchar *record,
DBUG_ENTER("update_dynamic_record");
flag=block_info.second_read=0;
+ /*
+ Check if we have enough room for the record.
+ First we do simplified check to make usual case faster.
+ Then we do more precise check for the space left.
+ Though it still is not absolutely precise, as
+ we always use MI_MAX_DYN_BLOCK_HEADER while it can be
+ less in the most of the cases.
+ */
+
+ /*
+ compare with just the reclength as we're going
+ to get some space from the old replaced record
+ */
+ if (unlikely(info->s->base.max_data_file_length -
+ info->state->data_file_length < reclength))
+ {
+ /*
+ let's read the old record's block to find out the length of the
+ old record
+ */
+ if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
+ & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | BLOCK_FATAL_ERROR))
+ {
+ DBUG_PRINT("error",("Got wrong block info"));
+ if (!(error & BLOCK_FATAL_ERROR))
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ goto err;
+ }
+
+ /*
+ if new record isn't longer, we can go on safely
+ */
+ if (block_info.rec_len < reclength)
+ {
+ if (info->s->base.max_data_file_length - info->state->data_file_length +
+ info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
+ reclength - block_info.rec_len + MI_MAX_DYN_BLOCK_HEADER)
+ {
+ my_errno=HA_ERR_RECORD_FILE_FULL;
+ goto err;
+ }
+ }
+ block_info.second_read=0;
+ }
+
while (reclength > 0)
{
if (filepos != info->s->state.dellink)
diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c
index b848c822f75..a4f6e1291db 100644
--- a/storage/myisam/mi_open.c
+++ b/storage/myisam/mi_open.c
@@ -697,8 +697,11 @@ uchar *mi_alloc_rec_buff(MI_INFO *info, ulong length, uchar **buf)
/* to simplify initial init of info->rec_buf in mi_open and mi_extra */
if (length == (ulong) -1)
{
- length= max(info->s->base.pack_reclength,
- info->s->base.max_key_length);
+ if (info->s->options & HA_OPTION_COMPRESS_RECORD)
+ length= max(info->s->base.pack_reclength, info->s->max_pack_length);
+ else
+ length= info->s->base.pack_reclength;
+ length= max(length, info->s->base.max_key_length);
/* Avoid unnecessary realloc */
if (newptr && length == old_length)
return newptr;
@@ -916,7 +919,7 @@ uchar *mi_state_info_read(uchar *ptr, MI_STATE_INFO *state)
key_blocks=state->header.max_block_size_index;
state->open_count = mi_uint2korr(ptr); ptr +=2;
- state->changed= (bool) *ptr++;
+ state->changed= *ptr++;
state->sortkey = (uint) *ptr++;
state->state.records= mi_rowkorr(ptr); ptr +=8;
state->state.del = mi_rowkorr(ptr); ptr +=8;
diff --git a/storage/myisam/mi_packrec.c b/storage/myisam/mi_packrec.c
index 305b7e5532c..59c98c978ce 100644
--- a/storage/myisam/mi_packrec.c
+++ b/storage/myisam/mi_packrec.c
@@ -166,7 +166,6 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys)
share->pack.header_length= uint4korr(header+4);
share->min_pack_length=(uint) uint4korr(header+8);
share->max_pack_length=(uint) uint4korr(header+12);
- set_if_bigger(share->base.pack_reclength,share->max_pack_length);
elements=uint4korr(header+16);
intervall_length=uint4korr(header+20);
trees=uint2korr(header+24);
@@ -565,7 +564,7 @@ static void fill_quick_table(uint16 *table, uint bits, uint max_bits,
*/
value|= (max_bits - bits) << 8 | IS_CHAR;
- for (end= table + (uint) (((uint) 1 << bits)); table < end; table++)
+ for (end= table + ((my_ptrdiff_t) 1 << bits); table < end; table++)
{
*table= (uint16) value;
}
diff --git a/storage/myisam/mi_write.c b/storage/myisam/mi_write.c
index 719008d3513..70ba7a4588a 100644
--- a/storage/myisam/mi_write.c
+++ b/storage/myisam/mi_write.c
@@ -987,7 +987,7 @@ int mi_init_bulk_insert(MI_INFO *info, ulong cache_size, ha_rows rows)
DBUG_RETURN(0);
if (rows && rows*total_keylength < cache_size)
- cache_size=rows;
+ cache_size= (ulong)rows;
else
cache_size/=total_keylength*16;
diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c
index 567e1057e5d..2300d1e7c23 100644
--- a/storage/myisam/myisamchk.c
+++ b/storage/myisam/myisamchk.c
@@ -1536,8 +1536,8 @@ static int mi_sort_records(MI_CHECK *param,
mi_check_print_error(param,"Not enough memory for key block");
goto err;
}
- if (!(sort_param.record=(uchar*) my_malloc((uint) share->base.pack_reclength,
- MYF(0))))
+
+ if (!mi_alloc_rec_buff(info, -1, &sort_param.record))
{
mi_check_print_error(param,"Not enough memory for record");
goto err;
@@ -1632,7 +1632,8 @@ err:
{
my_afree((uchar*) temp_buff);
}
- my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mi_get_rec_buff_ptr(info, sort_param.record),
+ MYF(MY_ALLOW_ZERO_PTR));
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
VOID(end_io_cache(&info->rec_cache));
my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
diff --git a/storage/myisam/myisampack.c b/storage/myisam/myisampack.c
index 37428ddd279..7233ebbff80 100644
--- a/storage/myisam/myisampack.c
+++ b/storage/myisam/myisampack.c
@@ -3148,7 +3148,7 @@ static void fakebigcodes(HUFF_COUNTS *huff_counts, HUFF_COUNTS *end_count)
cur_sort_p= sort_counts;
while (cur_count_p < end_count_p)
*(cur_sort_p++)= cur_count_p++;
- (void) qsort(sort_counts, 256, sizeof(my_off_t*), (qsort_cmp) fakecmp);
+ (void) my_qsort(sort_counts, 256, sizeof(my_off_t*), (qsort_cmp) fakecmp);
/*
Assign faked counts.
diff --git a/storage/myisam/rt_index.c b/storage/myisam/rt_index.c
index 63ed60586d6..25f9d7c19e4 100644
--- a/storage/myisam/rt_index.c
+++ b/storage/myisam/rt_index.c
@@ -483,15 +483,16 @@ static uchar *rtree_pick_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
uint key_length, uchar *page_buf, uint nod_flag)
{
double increase;
- double best_incr = DBL_MAX;
+ double best_incr;
double area;
double best_area;
- uchar *best_key;
+ uchar *best_key= NULL;
uchar *k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
uchar *last = rt_PAGE_END(page_buf);
LINT_INIT(best_area);
LINT_INIT(best_key);
+ LINT_INIT(best_incr);
for (; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag))
{
@@ -500,22 +501,13 @@ static uchar *rtree_pick_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
&area)) == -1.0)
return NULL;
/* The following should be safe, even if we compare doubles */
- if (increase < best_incr)
+ if (!best_key || increase < best_incr ||
+ ((increase == best_incr) && (area < best_area)))
{
best_key = k;
best_area = area;
best_incr = increase;
}
- else
- {
- /* The following should be safe, even if we compare doubles */
- if ((increase == best_incr) && (area < best_area))
- {
- best_key = k;
- best_area = area;
- best_incr = increase;
- }
- }
}
return best_key;
}
diff --git a/storage/myisam/rt_mbr.c b/storage/myisam/rt_mbr.c
index 1855e24feb0..deca23bbec7 100644
--- a/storage/myisam/rt_mbr.c
+++ b/storage/myisam/rt_mbr.c
@@ -523,7 +523,10 @@ double rtree_overlapping_area(HA_KEYSEG *keyseg, uchar* a, uchar* b,
}
/*
-Calculates MBR_AREA(a+b) - MBR_AREA(a)
+ Calculates MBR_AREA(a+b) - MBR_AREA(a)
+ Note: when 'a' and 'b' objects are far from each other,
+ the area increase can be really big, so this function
+ can return 'inf' as a result.
*/
double rtree_area_increase(HA_KEYSEG *keyseg, uchar* a, uchar* b,
uint key_length, double *ab_area)
diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c
index 27e4bd37af7..d505d2633ce 100644
--- a/storage/myisam/sort.c
+++ b/storage/myisam/sort.c
@@ -141,7 +141,7 @@ int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages,
if ((records < UINT_MAX32) &&
((my_off_t) (records + 1) *
(sort_length + sizeof(char*)) <= (my_off_t) memavl))
- keys= records+1;
+ keys= (uint)records+1;
else
do
{
@@ -349,7 +349,7 @@ pthread_handler_t thr_find_all_keys(void *arg)
sort_keys= (uchar **) NULL;
memavl= max(sort_param->sortbuff_size, MIN_SORT_MEMORY);
- idx= sort_param->sort_info->max_records;
+ idx= (uint)sort_param->sort_info->max_records;
sort_length= sort_param->key_length;
maxbuffer= 1;
@@ -565,9 +565,10 @@ int thr_write_keys(MI_SORT_PARAM *sort_param)
if (!mergebuf)
{
length=param->sort_buffer_length;
- while (length >= MIN_SORT_MEMORY && !mergebuf)
+ while (length >= MIN_SORT_MEMORY)
{
- mergebuf=my_malloc(length, MYF(0));
+ if ((mergebuf= my_malloc(length, MYF(0))))
+ break;
length=length*3/4;
}
if (!mergebuf)
@@ -649,8 +650,8 @@ static int NEAR_F write_keys(MI_SORT_PARAM *info, register uchar **sort_keys,
uint sort_length=info->key_length;
DBUG_ENTER("write_keys");
- qsort2((uchar*) sort_keys,count,sizeof(uchar*),(qsort2_cmp) info->key_cmp,
- info);
+ my_qsort2((uchar*) sort_keys,count,sizeof(uchar*),(qsort2_cmp) info->key_cmp,
+ info);
if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST",
DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
@@ -692,8 +693,8 @@ static int NEAR_F write_keys_varlen(MI_SORT_PARAM *info,
int err;
DBUG_ENTER("write_keys_varlen");
- qsort2((uchar*) sort_keys,count,sizeof(uchar*),(qsort2_cmp) info->key_cmp,
- info);
+ my_qsort2((uchar*) sort_keys,count,sizeof(uchar*),(qsort2_cmp) info->key_cmp,
+ info);
if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST",
DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
@@ -735,8 +736,8 @@ static int NEAR_F write_index(MI_SORT_PARAM *info, register uchar **sort_keys,
{
DBUG_ENTER("write_index");
- qsort2((uchar*) sort_keys,(size_t) count,sizeof(uchar*),
- (qsort2_cmp) info->key_cmp,info);
+ my_qsort2((uchar*) sort_keys,(size_t) count,sizeof(uchar*),
+ (qsort2_cmp) info->key_cmp,info);
while (count--)
{
if ((*info->key_write)(info,*sort_keys++))
@@ -904,6 +905,7 @@ merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
count=error=0;
maxcount=keys/((uint) (Tb-Fb) +1);
+ DBUG_ASSERT(maxcount > 0);
LINT_INIT(to_start_filepos);
if (to_file)
to_start_filepos=my_b_tell(to_file);
diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
index 8a914e8a2de..3fccb91e9b5 100644
--- a/storage/myisammrg/ha_myisammrg.cc
+++ b/storage/myisammrg/ha_myisammrg.cc
@@ -14,6 +14,82 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/*
+ MyISAM MERGE tables
+
+ A MyISAM MERGE table is kind of a union of zero or more MyISAM tables.
+
+ Besides the normal form file (.frm) a MERGE table has a meta file
+ (.MRG) with a list of tables. These are paths to the MyISAM table
+ files. The last two components of the path contain the database name
+ and the table name respectively.
+
+ When a MERGE table is open, there exists an TABLE object for the MERGE
+ table itself and a TABLE object for each of the MyISAM tables. For
+ abbreviated writing, I call the MERGE table object "parent" and the
+ MyISAM table objects "children".
+
+ A MERGE table is almost always opened through open_and_lock_tables()
+ and hence through open_tables(). When the parent appears in the list
+ of tables to open, the initial open of the handler does nothing but
+ read the meta file and collect a list of TABLE_LIST objects for the
+ children. This list is attached to the parent TABLE object as
+ TABLE::child_l. The end of the children list is saved in
+ TABLE::child_last_l.
+
+ Back in open_tables(), add_merge_table_list() is called. It updates
+ each list member with the lock type and a back pointer to the parent
+ TABLE_LIST object TABLE_LIST::parent_l. The list is then inserted in
+ the list of tables to open, right behind the parent. Consequently,
+ open_tables() opens the children, one after the other. The TABLE
+ references of the TABLE_LIST objects are implicitly set to the open
+ tables. The children are opened as independent MyISAM tables, right as
+ if they are used by the SQL statement.
+
+ TABLE_LIST::parent_l is required to find the parent 1. when the last
+ child has been opened and children are to be attached, and 2. when an
+ error happens during child open and the child list must be removed
+ from the queuery list. In these cases the current child does not have
+ TABLE::parent set or does not have a TABLE at all respectively.
+
+ When the last child is open, attach_merge_children() is called. It
+ removes the list of children from the open list. Then the children are
+ "attached" to the parent. All required references between parent and
+ children are set up.
+
+ The MERGE storage engine sets up an array with references to the
+ low-level MyISAM table objects (MI_INFO). It remembers the state of
+ the table in MYRG_INFO::children_attached.
+
+ Every child TABLE::parent references the parent TABLE object. That way
+ TABLE objects belonging to a MERGE table can be identified.
+ TABLE::parent is required because the parent and child TABLE objects
+ can live longer than the parent TABLE_LIST object. So the path
+ child->pos_in_table_list->parent_l->table can be broken.
+
+ If necessary, the compatibility of parent and children is checked.
+ This check is necessary when any of the objects are reopend. This is
+ detected by comparing the current table def version against the
+ remembered child def version. On parent open, the list members are
+ initialized to an "impossible"/"undefined" version value. So the check
+ is always executed on the first attach.
+
+ The version check is done in myisammrg_attach_children_callback(),
+ which is called for every child. ha_myisammrg::attach_children()
+ initializes 'need_compat_check' to FALSE and
+ myisammrg_attach_children_callback() sets it ot TRUE if a table
+ def version mismatches the remembered child def version.
+
+ Finally the parent TABLE::children_attached is set.
+
+ ---
+
+ On parent open the storage engine structures are allocated and initialized.
+ They stay with the open table until its final close.
+
+
+*/
+
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
@@ -22,14 +98,11 @@
#include "mysql_priv.h"
#include <mysql/plugin.h>
#include <m_ctype.h>
+#include "../myisam/ha_myisam.h"
#include "ha_myisammrg.h"
#include "myrg_def.h"
-/*****************************************************************************
-** MyISAM MERGE tables
-*****************************************************************************/
-
static handler *myisammrg_create_handler(handlerton *hton,
TABLE_SHARE *table,
MEM_ROOT *mem_root)
@@ -38,10 +111,23 @@ static handler *myisammrg_create_handler(handlerton *hton,
}
+/**
+ @brief Constructor
+*/
+
ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
:handler(hton, table_arg), file(0)
{}
+
+/**
+ @brief Destructor
+*/
+
+ha_myisammrg::~ha_myisammrg(void)
+{}
+
+
static const char *ha_myisammrg_exts[] = {
".MRG",
NullS
@@ -89,25 +175,311 @@ const char *ha_myisammrg::index_type(uint key_number)
}
-int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
+/**
+ @brief Callback function for open of a MERGE parent table.
+
+ @detail This function adds a TABLE_LIST object for a MERGE child table
+ to a list of tables of the parent TABLE object. It is called for
+ each child table.
+
+ The list of child TABLE_LIST objects is kept in the TABLE object of
+ the parent for the whole life time of the MERGE table. It is
+ inserted in the statement list behind the MERGE parent TABLE_LIST
+ object when the MERGE table is opened. It is removed from the
+ statement list after the last child is opened.
+
+ All memeory used for the child TABLE_LIST objects and the strings
+ referred by it are taken from the parent TABLE::mem_root. Thus they
+ are all freed implicitly at the final close of the table.
+
+ TABLE::child_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global
+ # # ^ # ^
+ # # | # |
+ # # +--------- TABLE_LIST::prev_global
+ # # |
+ # |<--- TABLE_LIST::prev_global |
+ # |
+ TABLE::child_last_l -----------------------------------------+
+
+ @param[in] callback_param data pointer as given to myrg_parent_open()
+ @param[in] filename file name of MyISAM table
+ without extension.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error
+*/
+
+static int myisammrg_parent_open_callback(void *callback_param,
+ const char *filename)
{
- MI_KEYDEF *keyinfo;
- MI_COLUMNDEF *recinfo;
- MYRG_TABLE *u_table;
- uint recs;
- uint keys= table->s->keys;
- int error;
- char name_buff[FN_REFLEN];
+ ha_myisammrg *ha_myrg;
+ TABLE *parent;
+ TABLE_LIST *child_l;
+ const char *db;
+ const char *table_name;
+ uint dirlen;
+ char dir_path[FN_REFLEN];
+ DBUG_ENTER("myisammrg_parent_open_callback");
+
+ /* Extract child table name and database name from filename. */
+ dirlen= dirname_length(filename);
+ if (dirlen >= FN_REFLEN)
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("error", ("name too long: '%.64s'", filename));
+ my_errno= ENAMETOOLONG;
+ DBUG_RETURN(1);
+ /* purecov: end */
+ }
+ table_name= filename + dirlen;
+ dirlen--; /* Strip off trailing '/'. */
+ memcpy(dir_path, filename, dirlen);
+ dir_path[dirlen]= '\0';
+ db= base_name(dir_path);
+ dirlen-= db - dir_path; /* This is now the length of 'db'. */
+ DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name));
+
+ ha_myrg= (ha_myisammrg*) callback_param;
+ parent= ha_myrg->table_ptr();
+
+ /* Get a TABLE_LIST object. */
+ if (!(child_l= (TABLE_LIST*) alloc_root(&parent->mem_root,
+ sizeof(TABLE_LIST))))
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("error", ("my_malloc error: %d", my_errno));
+ DBUG_RETURN(1);
+ /* purecov: end */
+ }
+ bzero((char*) child_l, sizeof(TABLE_LIST));
+
+ /* Set database (schema) name. */
+ child_l->db_length= dirlen;
+ child_l->db= strmake_root(&parent->mem_root, db, dirlen);
+ /* Set table name. */
+ child_l->table_name_length= strlen(table_name);
+ child_l->table_name= strmake_root(&parent->mem_root, table_name,
+ child_l->table_name_length);
+ /* Convert to lowercase if required. */
+ if (lower_case_table_names && child_l->table_name_length)
+ child_l->table_name_length= my_casedn_str(files_charset_info,
+ child_l->table_name);
+ /* Set alias. */
+ child_l->alias= child_l->table_name;
+
+ /* Initialize table map to 'undefined'. */
+ child_l->init_child_def_version();
+
+ /* Link TABLE_LIST object into the parent list. */
+ if (!parent->child_last_l)
+ {
+ /* Initialize parent->child_last_l when handling first child. */
+ parent->child_last_l= &parent->child_l;
+ }
+ *parent->child_last_l= child_l;
+ child_l->prev_global= parent->child_last_l;
+ parent->child_last_l= &child_l->next_global;
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief Callback function for attaching a MERGE child table.
+
+ @detail This function retrieves the MyISAM table handle from the
+ next child table. It is called for each child table.
+
+ @param[in] callback_param data pointer as given to
+ myrg_attach_children()
+
+ @return pointer to open MyISAM table structure
+ @retval !=NULL OK, returning pointer
+ @retval NULL, my_errno == 0 Ok, no more child tables
+ @retval NULL, my_errno != 0 error
+*/
+
+static MI_INFO *myisammrg_attach_children_callback(void *callback_param)
+{
+ ha_myisammrg *ha_myrg;
+ TABLE *parent;
+ TABLE *child;
+ TABLE_LIST *child_l;
+ MI_INFO *myisam;
+ DBUG_ENTER("myisammrg_attach_children_callback");
+
+ my_errno= 0;
+ ha_myrg= (ha_myisammrg*) callback_param;
+ parent= ha_myrg->table_ptr();
+
+ /* Get child list item. */
+ child_l= ha_myrg->next_child_attach;
+ if (!child_l)
+ {
+ DBUG_PRINT("myrg", ("No more children to attach"));
+ DBUG_RETURN(NULL);
+ }
+ child= child_l->table;
+ DBUG_PRINT("myrg", ("child table: '%s'.'%s' 0x%lx", child->s->db.str,
+ child->s->table_name.str, (long) child));
+ /*
+ Prepare for next child. Used as child_l in next call to this function.
+ We cannot rely on a NULL-terminated chain.
+ */
+ if (&child_l->next_global == parent->child_last_l)
+ {
+ DBUG_PRINT("myrg", ("attaching last child"));
+ ha_myrg->next_child_attach= NULL;
+ }
+ else
+ ha_myrg->next_child_attach= child_l->next_global;
+
+ /* Set parent reference. */
+ child->parent= parent;
+
+ /*
+ Do a quick compatibility check. The table def version is set when
+ the table share is created. The child def version is copied
+ from the table def version after a sucessful compatibility check.
+ We need to repeat the compatibility check only if a child is opened
+ from a different share than last time it was used with this MERGE
+ table.
+ */
+ DBUG_PRINT("myrg", ("table_def_version last: %lu current: %lu",
+ (ulong) child_l->get_child_def_version(),
+ (ulong) child->s->get_table_def_version()));
+ if (child_l->get_child_def_version() != child->s->get_table_def_version())
+ ha_myrg->need_compat_check= TRUE;
+
+ /*
+ If parent is temporary, children must be temporary too and vice
+ versa. This check must be done for every child on every open because
+ the table def version can overlap between temporary and
+ non-temporary tables. We need to detect the case where a
+ non-temporary table has been replaced with a temporary table of the
+ same version. Or vice versa. A very unlikely case, but it could
+ happen.
+ */
+ if (child->s->tmp_table != parent->s->tmp_table)
+ {
+ DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d",
+ parent->s->tmp_table, child->s->tmp_table));
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ goto err;
+ }
+
+ /* Extract the MyISAM table structure pointer from the handler object. */
+ if ((child->file->ht->db_type != DB_TYPE_MYISAM) ||
+ !(myisam= ((ha_myisam*) child->file)->file_ptr()))
+ {
+ DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' 0x%lx",
+ child->s->db.str, child->s->table_name.str,
+ (long) child));
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ }
+ DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx my_errno: %d",
+ (long) myisam, my_errno));
+
+ err:
+ DBUG_RETURN(my_errno ? NULL : myisam);
+}
+
+
+/**
+ @brief Open a MERGE parent table, not its children.
+
+ @detail This function initializes the MERGE storage engine structures
+ and adds a child list of TABLE_LIST to the parent TABLE.
+
+ @param[in] name MERGE table path name
+ @param[in] mode read/write mode, unused
+ @param[in] test_if_locked open flags
+
+ @return status
+ @retval 0 OK
+ @retval -1 Error, my_errno gives reason
+*/
+
+int ha_myisammrg::open(const char *name, int mode __attribute__((unused)),
+ uint test_if_locked)
+{
+ DBUG_ENTER("ha_myisammrg::open");
+ DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table));
+ DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked));
+
+ /* Save for later use. */
+ this->test_if_locked= test_if_locked;
+
+ /* retrieve children table list. */
+ my_errno= 0;
+ if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this)))
+ {
+ DBUG_PRINT("error", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
+ }
+ DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx", (long) file));
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief Attach children to a MERGE table.
+
+ @detail Let the storage engine attach its children through a callback
+ function. Check table definitions for consistency.
+
+ @note Special thd->open_options may be in effect. We can make use of
+ them in attach. I.e. we use HA_OPEN_FOR_REPAIR to report the names
+ of mismatching child tables. We cannot transport these options in
+ ha_myisammrg::test_if_locked because they may change after the
+ parent is opened. The parent is kept open in the table cache over
+ multiple statements and can be used by other threads. Open options
+ can change over time.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error, my_errno gives reason
+*/
+
+int ha_myisammrg::attach_children(void)
+{
+ MYRG_TABLE *u_table;
+ MI_COLUMNDEF *recinfo;
+ MI_KEYDEF *keyinfo;
+ uint recs;
+ uint keys= table->s->keys;
+ int error;
+ DBUG_ENTER("ha_myisammrg::attach_children");
+ DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
+ table->s->table_name.str, (long) table));
+ DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
+ DBUG_ASSERT(!this->file->children_attached);
- DBUG_PRINT("info", ("ha_myisammrg::open"));
- if (!(file=myrg_open(fn_format(name_buff,name,"","",
- MY_UNPACK_FILENAME|MY_APPEND_EXT),
- mode, test_if_locked)))
+ /*
+ Initialize variables that are used, modified, and/or set by
+ myisammrg_attach_children_callback().
+ 'next_child_attach' traverses the chain of TABLE_LIST objects
+ that has been compiled during myrg_parent_open(). Every call
+ to myisammrg_attach_children_callback() moves the pointer to
+ the next object.
+ 'need_compat_check' is set by myisammrg_attach_children_callback()
+ if a child fails the table def version check.
+ 'my_errno' is set by myisammrg_attach_children_callback() in
+ case of an error.
+ */
+ next_child_attach= table->child_l;
+ need_compat_check= FALSE;
+ my_errno= 0;
+
+ if (myrg_attach_children(this->file, this->test_if_locked |
+ current_thd->open_options,
+ myisammrg_attach_children_callback, this))
{
- DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno));
- return (my_errno ? my_errno : -1);
+ DBUG_PRINT("error", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
}
- DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc..."));
+ DBUG_PRINT("myrg", ("calling myrg_extrafunc"));
myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
@@ -116,69 +488,147 @@ int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
- if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
- {
- DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu",
- table->s->reclength, stats.mean_rec_length));
- if (test_if_locked & HA_OPEN_FOR_REPAIR)
- myrg_print_wrong_table(file->open_tables->table->filename);
- error= HA_ERR_WRONG_MRG_TABLE_DEF;
- goto err;
- }
- if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
- {
- /* purecov: begin inspected */
- DBUG_PRINT("error", ("Failed to convert TABLE object to MyISAM "
- "key and column definition"));
- goto err;
- /* purecov: end */
- }
- for (u_table= file->open_tables; u_table < file->end_table; u_table++)
+ /*
+ The compatibility check is required only if one or more children do
+ not match their table def version from the last check. This will
+ always happen at the first attach because the reference child def
+ version is initialized to 'undefined' at open.
+ */
+ DBUG_PRINT("myrg", ("need_compat_check: %d", need_compat_check));
+ if (need_compat_check)
{
- if (check_definition(keyinfo, recinfo, keys, recs,
- u_table->table->s->keyinfo, u_table->table->s->rec,
- u_table->table->s->base.keys,
- u_table->table->s->base.fields, false))
+ TABLE_LIST *child_l;
+
+ if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
{
- error= HA_ERR_WRONG_MRG_TABLE_DEF;
+ DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu",
+ table->s->reclength, stats.mean_rec_length));
if (test_if_locked & HA_OPEN_FOR_REPAIR)
- myrg_print_wrong_table(u_table->table->filename);
- else
+ myrg_print_wrong_table(file->open_tables->table->filename);
+ error= HA_ERR_WRONG_MRG_TABLE_DEF;
+ goto err;
+ }
+ /*
+ Both recinfo and keyinfo are allocated by my_multi_malloc(), thus
+ only recinfo must be freed.
+ */
+ if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
+ "key and column definition"));
+ goto err;
+ /* purecov: end */
+ }
+ for (u_table= file->open_tables; u_table < file->end_table; u_table++)
+ {
+ if (check_definition(keyinfo, recinfo, keys, recs,
+ u_table->table->s->keyinfo, u_table->table->s->rec,
+ u_table->table->s->base.keys,
+ u_table->table->s->base.fields, false))
{
- my_free((uchar*) recinfo, MYF(0));
- goto err;
+ DBUG_PRINT("error", ("table definition mismatch: '%s'",
+ u_table->table->filename));
+ error= HA_ERR_WRONG_MRG_TABLE_DEF;
+ if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
+ {
+ my_free((uchar*) recinfo, MYF(0));
+ goto err;
+ }
+ myrg_print_wrong_table(u_table->table->filename);
}
}
+ my_free((uchar*) recinfo, MYF(0));
+ if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
+ goto err;
+
+ /* All checks passed so far. Now update child def version. */
+ for (child_l= table->child_l; ; child_l= child_l->next_global)
+ {
+ child_l->set_child_def_version(
+ child_l->table->s->get_table_def_version());
+
+ if (&child_l->next_global == table->child_last_l)
+ break;
+ }
}
- my_free((uchar*) recinfo, MYF(0));
- if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
- goto err;
#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
/* Merge table has more than 2G rows */
if (table->s->crashed)
{
+ DBUG_PRINT("error", ("MERGE table marked crashed"));
error= HA_ERR_WRONG_MRG_TABLE_DEF;
goto err;
}
#endif
- return (0);
+ DBUG_RETURN(0);
+
err:
- myrg_close(file);
- file=0;
- return (my_errno= error);
+ myrg_detach_children(file);
+ DBUG_RETURN(my_errno= error);
+}
+
+
+/**
+ @brief Detach all children from a MERGE table.
+
+ @note Detach must not touch the children in any way.
+ They may have been closed at ths point already.
+ All references to the children should be removed.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error, my_errno gives reason
+*/
+
+int ha_myisammrg::detach_children(void)
+{
+ DBUG_ENTER("ha_myisammrg::detach_children");
+ DBUG_ASSERT(this->file && this->file->children_attached);
+
+ if (myrg_detach_children(this->file))
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("error", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
+ /* purecov: end */
+ }
+ DBUG_RETURN(0);
}
+
+/**
+ @brief Close a MERGE parent table, not its children.
+
+ @note The children are expected to be closed separately by the caller.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error, my_errno gives reason
+*/
+
int ha_myisammrg::close(void)
{
- return myrg_close(file);
+ int rc;
+ DBUG_ENTER("ha_myisammrg::close");
+ /*
+ Children must not be attached here. Unless the MERGE table has no
+ children. In this case children_attached is always true.
+ */
+ DBUG_ASSERT(!this->file->children_attached || !this->file->tables);
+ rc= myrg_close(file);
+ file= 0;
+ DBUG_RETURN(rc);
}
int ha_myisammrg::write_row(uchar * buf)
{
+ DBUG_ENTER("ha_myisammrg::write_row");
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_write_count);
if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
- return (HA_ERR_TABLE_READONLY);
+ DBUG_RETURN(HA_ERR_TABLE_READONLY);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
@@ -186,13 +636,14 @@ int ha_myisammrg::write_row(uchar * buf)
{
int error;
if ((error= update_auto_increment()))
- return error;
+ DBUG_RETURN(error); /* purecov: inspected */
}
- return myrg_write(file,buf);
+ DBUG_RETURN(myrg_write(file,buf));
}
int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_update_count);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
table->timestamp_field->set_time();
@@ -201,6 +652,7 @@ int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data)
int ha_myisammrg::delete_row(const uchar * buf)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_delete_count);
return myrg_delete(file,buf);
}
@@ -209,6 +661,7 @@ int ha_myisammrg::index_read_map(uchar * buf, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_key_count);
int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag);
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -219,6 +672,7 @@ int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_key_count);
int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag);
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -228,6 +682,7 @@ int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
key_part_map keypart_map)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_key_count);
int error=myrg_rkey(file,buf,active_index, key, keypart_map,
HA_READ_PREFIX_LAST);
@@ -237,6 +692,7 @@ int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
int ha_myisammrg::index_next(uchar * buf)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_next_count);
int error=myrg_rnext(file,buf,active_index);
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -245,6 +701,7 @@ int ha_myisammrg::index_next(uchar * buf)
int ha_myisammrg::index_prev(uchar * buf)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_prev_count);
int error=myrg_rprev(file,buf, active_index);
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -253,6 +710,7 @@ int ha_myisammrg::index_prev(uchar * buf)
int ha_myisammrg::index_first(uchar * buf)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_first_count);
int error=myrg_rfirst(file, buf, active_index);
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -261,6 +719,7 @@ int ha_myisammrg::index_first(uchar * buf)
int ha_myisammrg::index_last(uchar * buf)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_last_count);
int error=myrg_rlast(file, buf, active_index);
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -271,8 +730,13 @@ int ha_myisammrg::index_next_same(uchar * buf,
const uchar *key __attribute__((unused)),
uint length __attribute__((unused)))
{
+ int error;
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_next_count);
- int error=myrg_rnext_same(file,buf);
+ do
+ {
+ error= myrg_rnext_same(file,buf);
+ } while (error == HA_ERR_RECORD_DELETED);
table->status=error ? STATUS_NOT_FOUND: 0;
return error;
}
@@ -280,12 +744,14 @@ int ha_myisammrg::index_next_same(uchar * buf,
int ha_myisammrg::rnd_init(bool scan)
{
+ DBUG_ASSERT(this->file->children_attached);
return myrg_reset(file);
}
int ha_myisammrg::rnd_next(uchar *buf)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_rnd_next_count);
int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -295,6 +761,7 @@ int ha_myisammrg::rnd_next(uchar *buf)
int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
{
+ DBUG_ASSERT(this->file->children_attached);
ha_statistic_increment(&SSV::ha_read_rnd_count);
int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length));
table->status=error ? STATUS_NOT_FOUND: 0;
@@ -303,6 +770,7 @@ int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
void ha_myisammrg::position(const uchar *record)
{
+ DBUG_ASSERT(this->file->children_attached);
ulonglong row_position= myrg_position(file);
my_store_ptr(ref, ref_length, (my_off_t) row_position);
}
@@ -311,6 +779,7 @@ void ha_myisammrg::position(const uchar *record)
ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
key_range *max_key)
{
+ DBUG_ASSERT(this->file->children_attached);
return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key);
}
@@ -318,6 +787,7 @@ ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
int ha_myisammrg::info(uint flag)
{
MYMERGE_INFO mrg_info;
+ DBUG_ASSERT(this->file->children_attached);
(void) myrg_status(file,&mrg_info,flag);
/*
The following fails if one has not compiled MySQL with -DBIG_TABLES
@@ -387,6 +857,23 @@ int ha_myisammrg::info(uint flag)
int ha_myisammrg::extra(enum ha_extra_function operation)
{
+ if (operation == HA_EXTRA_ATTACH_CHILDREN)
+ {
+ int rc= attach_children();
+ if (!rc)
+ (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
+ return(rc);
+ }
+ else if (operation == HA_EXTRA_DETACH_CHILDREN)
+ {
+ /*
+ Note that detach must not touch the children in any way.
+ They may have been closed at ths point already.
+ */
+ int rc= detach_children();
+ return(rc);
+ }
+
/* As this is just a mapping, we don't have to force the underlying
tables to be closed */
if (operation == HA_EXTRA_FORCE_REOPEN ||
@@ -404,6 +891,7 @@ int ha_myisammrg::reset(void)
int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
+ DBUG_ASSERT(this->file->children_attached);
if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
return 0;
return myrg_extra(file, operation, (void*) &cache_size);
@@ -411,11 +899,20 @@ int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
int ha_myisammrg::external_lock(THD *thd, int lock_type)
{
+ DBUG_ASSERT(this->file->children_attached);
return myrg_lock_database(file,lock_type);
}
uint ha_myisammrg::lock_count(void) const
{
+ /*
+ Return the real lock count even if the children are not attached.
+ This method is used for allocating memory. If we would return 0
+ to another thread (e.g. doing FLUSH TABLE), and attach the children
+ before the other thread calls store_lock(), then we would return
+ more locks in store_lock() than we claimed by lock_count(). The
+ other tread would overrun its memory.
+ */
return file->tables;
}
@@ -426,6 +923,24 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
{
MYRG_TABLE *open_table;
+ /*
+ This method can be called while another thread is attaching the
+ children. If the processor reorders instructions or write to memory,
+ 'children_attached' could be set before 'open_tables' has all the
+ pointers to the children. Use of a mutex here and in
+ myrg_attach_children() forces consistent data.
+ */
+ pthread_mutex_lock(&this->file->mutex);
+
+ /*
+ When MERGE table is open, but not yet attached, other threads
+ could flush it, which means call mysql_lock_abort_for_thread()
+ on this threads TABLE. 'children_attached' is FALSE in this
+ situaton. Since the table is not locked, return no lock data.
+ */
+ if (!this->file->children_attached)
+ goto end; /* purecov: tested */
+
for (open_table=file->open_tables ;
open_table != file->end_table ;
open_table++)
@@ -434,6 +949,9 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK)
open_table->table->lock.type=lock_type;
}
+
+ end:
+ pthread_mutex_unlock(&this->file->mutex);
return to;
}
@@ -519,47 +1037,50 @@ int ha_myisammrg::create(const char *name, register TABLE *form,
uint dirlgt= dirname_length(name);
DBUG_ENTER("ha_myisammrg::create");
+ /* Allocate a table_names array in thread mem_root. */
if (!(table_names= (const char**)
thd->alloc((create_info->merge_list.elements+1) * sizeof(char*))))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ /* Create child path names. */
for (pos= table_names; tables; tables= tables->next_local)
{
const char *table_name;
- TABLE *tbl= 0;
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
- tbl= find_temporary_table(thd, tables);
- if (!tbl)
- {
- /*
- Construct the path to the MyISAM table. Try to meet two conditions:
- 1.) Allow to include MyISAM tables from different databases, and
- 2.) allow for moving DATADIR around in the file system.
- The first means that we need paths in the .MRG file. The second
- means that we should not have absolute paths in the .MRG file.
- The best, we can do, is to use 'mysql_data_home', which is '.'
- in mysqld and may be an absolute path in an embedded server.
- This means that it might not be possible to move the DATADIR of
- an embedded server without changing the paths in the .MRG file.
- */
- uint length= build_table_filename(buff, sizeof(buff),
- tables->db, tables->table_name, "", 0);
- /*
- If a MyISAM table is in the same directory as the MERGE table,
- we use the table name without a path. This means that the
- DATADIR can easily be moved even for an embedded server as long
- as the MyISAM tables are from the same database as the MERGE table.
- */
- if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
- table_name= tables->table_name;
- else
- if (! (table_name= thd->strmake(buff, length)))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- }
+
+ /*
+ Construct the path to the MyISAM table. Try to meet two conditions:
+ 1.) Allow to include MyISAM tables from different databases, and
+ 2.) allow for moving DATADIR around in the file system.
+ The first means that we need paths in the .MRG file. The second
+ means that we should not have absolute paths in the .MRG file.
+ The best, we can do, is to use 'mysql_data_home', which is '.'
+ in mysqld and may be an absolute path in an embedded server.
+ This means that it might not be possible to move the DATADIR of
+ an embedded server without changing the paths in the .MRG file.
+
+ Do the same even for temporary tables. MERGE children are now
+ opened through the table cache. They are opened by db.table_name,
+ not by their path name.
+ */
+ uint length= build_table_filename(buff, sizeof(buff),
+ tables->db, tables->table_name, "", 0);
+ /*
+ If a MyISAM table is in the same directory as the MERGE table,
+ we use the table name without a path. This means that the
+ DATADIR can easily be moved even for an embedded server as long
+ as the MyISAM tables are from the same database as the MERGE table.
+ */
+ if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
+ table_name= tables->table_name;
else
- table_name= tbl->s->path.str;
+ if (! (table_name= thd->strmake(buff, length)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
+
*pos++= table_name;
}
*pos=0;
+
+ /* Create a MERGE meta file from the table_names array. */
DBUG_RETURN(myrg_create(fn_format(buff,name,"","",
MY_RESOLVE_SYMLINKS|
MY_UNPACK_FILENAME|MY_APPEND_EXT),
@@ -642,7 +1163,7 @@ static int myisammrg_init(void *p)
myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM;
myisammrg_hton->create= myisammrg_create_handler;
myisammrg_hton->panic= myisammrg_panic;
- myisammrg_hton->flags= HTON_CAN_RECREATE|HTON_NO_PARTITION;
+ myisammrg_hton->flags= HTON_NO_PARTITION;
return 0;
}
diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h
index 91aabe277f7..977c45d1435 100644
--- a/storage/myisammrg/ha_myisammrg.h
+++ b/storage/myisammrg/ha_myisammrg.h
@@ -27,8 +27,12 @@ class ha_myisammrg: public handler
MYRG_INFO *file;
public:
+ TABLE_LIST *next_child_attach; /* next child to attach */
+ uint test_if_locked; /* flags from ::open() */
+ bool need_compat_check; /* if need compatibility check */
+
ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg);
- ~ha_myisammrg() {}
+ ~ha_myisammrg();
const char *table_type() const { return "MRG_MyISAM"; }
const char **bas_ext() const;
const char *index_type(uint key_number);
@@ -53,6 +57,8 @@ class ha_myisammrg: public handler
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; }
int open(const char *name, int mode, uint test_if_locked);
+ int attach_children(void);
+ int detach_children(void);
int close(void);
int write_row(uchar * buf);
int update_row(const uchar * old_data, uchar * new_data);
@@ -85,6 +91,7 @@ class ha_myisammrg: public handler
void update_create_info(HA_CREATE_INFO *create_info);
void append_create_info(String *packet);
MYRG_INFO *myrg_info() { return file; }
+ TABLE *table_ptr() { return table; }
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
int check(THD* thd, HA_CHECK_OPT* check_opt);
};
diff --git a/storage/myisammrg/myrg_close.c b/storage/myisammrg/myrg_close.c
index baae24634b3..97216ed47fe 100644
--- a/storage/myisammrg/myrg_close.c
+++ b/storage/myisammrg/myrg_close.c
@@ -23,13 +23,42 @@ int myrg_close(MYRG_INFO *info)
MYRG_TABLE *file;
DBUG_ENTER("myrg_close");
- for (file=info->open_tables ; file != info->end_table ; file++)
- if ((new_error=mi_close(file->table)))
- error=new_error;
+ /*
+ Assume that info->children_attached means that this is called from
+ direct use of MERGE, not from a MySQL server. In this case the
+ children must be closed and info->rec_per_key_part is part of the
+ 'info' multi_alloc.
+ If info->children_attached is false, this is called from a MySQL
+ server. Children are closed independently but info->rec_per_key_part
+ must be freed.
+ Just in case of a server panic (myrg_panic()) info->children_attached
+ might be true. We would close the children though they should be
+ closed independently and info->rec_per_key_part is not freed.
+ This should be acceptable for a panic.
+ In case of a MySQL server and no children, children_attached is
+ always true. In this case no rec_per_key_part has been allocated.
+ So it is correct to use the branch where an empty list of tables is
+ (not) closed.
+ */
+ if (info->children_attached)
+ {
+ for (file= info->open_tables; file != info->end_table; file++)
+ {
+ /* purecov: begin inspected */
+ if ((new_error= mi_close(file->table)))
+ error= new_error;
+ else
+ file->table= NULL;
+ /* purecov: end */
+ }
+ }
+ else
+ my_free((uchar*) info->rec_per_key_part, MYF(MY_ALLOW_ZERO_PTR));
delete_queue(&info->by_key);
pthread_mutex_lock(&THR_LOCK_open);
myrg_open_list=list_delete(myrg_open_list,&info->open_list);
pthread_mutex_unlock(&THR_LOCK_open);
+ VOID(pthread_mutex_destroy(&info->mutex));
my_free((uchar*) info,MYF(0));
if (error)
{
diff --git a/storage/myisammrg/myrg_extra.c b/storage/myisammrg/myrg_extra.c
index a1e6e3f8d4d..3d14f6a56e6 100644
--- a/storage/myisammrg/myrg_extra.c
+++ b/storage/myisammrg/myrg_extra.c
@@ -29,6 +29,8 @@ int myrg_extra(MYRG_INFO *info,enum ha_extra_function function,
DBUG_ENTER("myrg_extra");
DBUG_PRINT("info",("function: %lu", (ulong) function));
+ if (!info->children_attached)
+ DBUG_RETURN(1);
if (function == HA_EXTRA_CACHE)
{
info->cache_in_use=1;
@@ -73,6 +75,8 @@ int myrg_reset(MYRG_INFO *info)
MYRG_TABLE *file;
DBUG_ENTER("myrg_reset");
+ if (!info->children_attached)
+ DBUG_RETURN(1);
info->cache_in_use=0;
info->current_table=0;
info->last_used_table= info->open_tables;
diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c
index 500d3a29327..b5002116164 100644
--- a/storage/myisammrg/myrg_open.c
+++ b/storage/myisammrg/myrg_open.c
@@ -26,8 +26,14 @@
open a MyISAM MERGE table
if handle_locking is 0 then exit with error if some table is locked
if handle_locking is 1 then wait if table is locked
-*/
+ NOTE: This function is not used in the MySQL server. It is for
+ MERGE use independent from MySQL. Currently there is some code
+ duplication between myrg_open() and myrg_parent_open() +
+ myrg_attach_children(). Please duplicate changes in these
+ functions or make common sub-functions.
+*/
+/* purecov: begin deadcode */ /* not used in MySQL server */
MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
{
@@ -107,13 +113,11 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
key_parts*sizeof(long),
MYF(MY_WME|MY_ZEROFILL))))
goto err;
- if (files)
- {
- m_info->open_tables=(MYRG_TABLE *) (m_info+1);
- m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
- m_info->tables= files;
- files= 0;
- }
+ DBUG_ASSERT(files);
+ m_info->open_tables=(MYRG_TABLE *) (m_info+1);
+ m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
+ m_info->tables= files;
+ files= 0;
m_info->reclength=isam->s->base.reclength;
min_keys= isam->s->base.keys;
errpos=3;
@@ -163,9 +167,11 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
/* this works ok if the table list is empty */
m_info->end_table=m_info->open_tables+files;
m_info->last_used_table=m_info->open_tables;
+ m_info->children_attached= TRUE;
VOID(my_close(fd,MYF(0)));
end_io_cache(&file);
+ VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
m_info->open_list.data=(void*) m_info;
pthread_mutex_lock(&THR_LOCK_open);
myrg_open_list=list_add(myrg_open_list,&m_info->open_list);
@@ -189,3 +195,330 @@ err:
my_errno=save_errno;
DBUG_RETURN (NULL);
}
+/* purecov: end */
+
+
+/**
+ @brief Open parent table of a MyISAM MERGE table.
+
+ @detail Open MERGE meta file to get the table name paths for the child
+ tables. Count the children. Allocate and initialize MYRG_INFO
+ structure. Call a callback function for each child table.
+
+ @param[in] parent_name merge table name path as "database/table"
+ @param[in] callback function to call for each child table
+ @param[in] callback_param data pointer to give to the callback
+
+ @return MYRG_INFO pointer
+ @retval != NULL OK
+ @retval NULL Error
+
+ @note: Currently there is some code duplication between myrg_open()
+ and myrg_parent_open() + myrg_attach_children(). Please duplicate
+ changes in these functions or make common sub-functions.
+*/
+
+MYRG_INFO *myrg_parent_open(const char *parent_name,
+ int (*callback)(void*, const char*),
+ void *callback_param)
+{
+ MYRG_INFO *m_info;
+ int rc;
+ int errpos;
+ int save_errno;
+ int insert_method;
+ uint length;
+ uint dir_length;
+ uint child_count;
+ size_t name_buff_length;
+ File fd;
+ IO_CACHE file_cache;
+ char parent_name_buff[FN_REFLEN * 2];
+ char child_name_buff[FN_REFLEN];
+ DBUG_ENTER("myrg_parent_open");
+
+ rc= 1;
+ errpos= 0;
+ bzero((char*) &file_cache, sizeof(file_cache));
+
+ /* Open MERGE meta file. */
+ if ((fd= my_open(fn_format(parent_name_buff, parent_name, "", MYRG_NAME_EXT,
+ MY_UNPACK_FILENAME|MY_APPEND_EXT),
+ O_RDONLY | O_SHARE, MYF(0))) < 0)
+ goto err; /* purecov: inspected */
+ errpos= 1;
+
+ if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0,
+ MYF(MY_WME | MY_NABP)))
+ goto err; /* purecov: inspected */
+ errpos= 2;
+
+ /* Count children. Determine insert method. */
+ child_count= 0;
+ insert_method= 0;
+ while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
+ {
+ /* Remove line terminator. */
+ if (child_name_buff[length - 1] == '\n')
+ child_name_buff[--length]= '\0';
+
+ /* Skip empty lines. */
+ if (!child_name_buff[0])
+ continue; /* purecov: inspected */
+
+ /* Skip comments, but evaluate insert method. */
+ if (child_name_buff[0] == '#')
+ {
+ if (!strncmp(child_name_buff + 1, "INSERT_METHOD=", 14))
+ {
+ /* Compare buffer with global methods list: merge_insert_method. */
+ insert_method= find_type(child_name_buff + 15,
+ &merge_insert_method, 2);
+ }
+ continue;
+ }
+
+ /* Count the child. */
+ child_count++;
+ }
+
+ /* Allocate MERGE parent table structure. */
+ if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
+ child_count * sizeof(MYRG_TABLE),
+ MYF(MY_WME | MY_ZEROFILL))))
+ goto err; /* purecov: inspected */
+ errpos= 3;
+ m_info->open_tables= (MYRG_TABLE*) (m_info + 1);
+ m_info->tables= child_count;
+ m_info->merge_insert_method= insert_method > 0 ? insert_method : 0;
+ /* This works even if the table list is empty. */
+ m_info->end_table= m_info->open_tables + child_count;
+ if (!child_count)
+ {
+ /* Do not attach/detach an empty child list. */
+ m_info->children_attached= TRUE;
+ }
+
+ /* Call callback for each child. */
+ dir_length= dirname_part(parent_name_buff, parent_name, &name_buff_length);
+ my_b_seek(&file_cache, 0);
+ while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
+ {
+ /* Remove line terminator. */
+ if (child_name_buff[length - 1] == '\n')
+ child_name_buff[--length]= '\0';
+
+ /* Skip empty lines and comments. */
+ if (!child_name_buff[0] || (child_name_buff[0] == '#'))
+ continue;
+
+ if (!has_path(child_name_buff))
+ {
+ VOID(strmake(parent_name_buff + dir_length, child_name_buff,
+ sizeof(parent_name_buff) - 1 - dir_length));
+ VOID(cleanup_dirname(child_name_buff, parent_name_buff));
+ }
+ else
+ fn_format(child_name_buff, child_name_buff, "", "", 0);
+ DBUG_PRINT("info", ("child: '%s'", child_name_buff));
+
+ /* Callback registers child with handler table. */
+ if ((rc= (*callback)(callback_param, child_name_buff)))
+ goto err; /* purecov: inspected */
+ }
+
+ end_io_cache(&file_cache);
+ VOID(my_close(fd, MYF(0)));
+ VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
+
+ m_info->open_list.data= (void*) m_info;
+ pthread_mutex_lock(&THR_LOCK_open);
+ myrg_open_list= list_add(myrg_open_list, &m_info->open_list);
+ pthread_mutex_unlock(&THR_LOCK_open);
+
+ DBUG_RETURN(m_info);
+
+ /* purecov: begin inspected */
+ err:
+ save_errno= my_errno;
+ switch (errpos) {
+ case 3:
+ my_free((char*) m_info, MYF(0));
+ /* Fall through */
+ case 2:
+ end_io_cache(&file_cache);
+ /* Fall through */
+ case 1:
+ VOID(my_close(fd, MYF(0)));
+ }
+ my_errno= save_errno;
+ DBUG_RETURN (NULL);
+ /* purecov: end */
+}
+
+
+/**
+ @brief Attach children to a MyISAM MERGE parent table.
+
+ @detail Call a callback function for each child table.
+ The callback returns the MyISAM table handle of the child table.
+ Check table definition match.
+
+ @param[in] m_info MERGE parent table structure
+ @param[in] handle_locking if contains HA_OPEN_FOR_REPAIR, warn about
+ incompatible child tables, but continue
+ @param[in] callback function to call for each child table
+ @param[in] callback_param data pointer to give to the callback
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error
+
+ @note: Currently there is some code duplication between myrg_open()
+ and myrg_parent_open() + myrg_attach_children(). Please duplicate
+ changes in these functions or make common sub-functions.
+*/
+
+int myrg_attach_children(MYRG_INFO *m_info, int handle_locking,
+ MI_INFO *(*callback)(void*),
+ void *callback_param)
+{
+ ulonglong file_offset;
+ MI_INFO *myisam;
+ int rc;
+ int errpos;
+ int save_errno;
+ uint idx;
+ uint child_nr;
+ uint key_parts;
+ uint min_keys;
+ DBUG_ENTER("myrg_attach_children");
+ DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking));
+
+ /*
+ This function can be called while another thread is trying to abort
+ locks of this MERGE table. If the processor reorders instructions or
+ write to memory, 'children_attached' could be set before
+ 'open_tables' has all the pointers to the children. Use of a mutex
+ here and in ha_myisammrg::store_lock() forces consistent data.
+ */
+ pthread_mutex_lock(&m_info->mutex);
+ rc= 1;
+ errpos= 0;
+ file_offset= 0;
+ LINT_INIT(key_parts);
+ min_keys= 0;
+ child_nr= 0;
+ while ((myisam= (*callback)(callback_param)))
+ {
+ DBUG_PRINT("myrg", ("child_nr: %u table: '%s'",
+ child_nr, myisam->filename));
+ DBUG_ASSERT(child_nr < m_info->tables);
+
+ /* Special handling when the first child is attached. */
+ if (!child_nr)
+ {
+ m_info->reclength= myisam->s->base.reclength;
+ min_keys= myisam->s->base.keys;
+ key_parts= myisam->s->base.key_parts;
+ if (!m_info->rec_per_key_part)
+ {
+ if(!(m_info->rec_per_key_part= (ulong*)
+ my_malloc(key_parts * sizeof(long), MYF(MY_WME|MY_ZEROFILL))))
+ goto err; /* purecov: inspected */
+ errpos= 1;
+ }
+ }
+
+ /* Add MyISAM table info. */
+ m_info->open_tables[child_nr].table= myisam;
+ m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset;
+ file_offset+= myisam->state->data_file_length;
+
+ /* Check table definition match. */
+ if (m_info->reclength != myisam->s->base.reclength)
+ {
+ DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d",
+ myisam->filename,
+ (handle_locking & HA_OPEN_FOR_REPAIR)));
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ if (handle_locking & HA_OPEN_FOR_REPAIR)
+ {
+ myrg_print_wrong_table(myisam->filename);
+ continue;
+ }
+ goto err;
+ }
+
+ m_info->options|= myisam->s->options;
+ m_info->records+= myisam->state->records;
+ m_info->del+= myisam->state->del;
+ m_info->data_file_length+= myisam->state->data_file_length;
+ if (min_keys > myisam->s->base.keys)
+ min_keys= myisam->s->base.keys; /* purecov: inspected */
+ for (idx= 0; idx < key_parts; idx++)
+ m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] /
+ m_info->tables);
+ child_nr++;
+ }
+
+ if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
+ goto err;
+ if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
+ {
+ my_errno= HA_ERR_RECORD_FILE_FULL;
+ goto err;
+ }
+ /* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
+ m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
+ m_info->keys= min_keys;
+ m_info->last_used_table= m_info->open_tables;
+ m_info->children_attached= TRUE;
+ pthread_mutex_unlock(&m_info->mutex);
+ DBUG_RETURN(0);
+
+err:
+ save_errno= my_errno;
+ switch (errpos) {
+ case 1:
+ my_free((char*) m_info->rec_per_key_part, MYF(0));
+ m_info->rec_per_key_part= NULL;
+ }
+ pthread_mutex_unlock(&m_info->mutex);
+ my_errno= save_errno;
+ DBUG_RETURN(1);
+}
+
+
+/**
+ @brief Detach children from a MyISAM MERGE parent table.
+
+ @param[in] m_info MERGE parent table structure
+
+ @note Detach must not touch the children in any way.
+ They may have been closed at ths point already.
+ All references to the children should be removed.
+
+ @return status
+ @retval 0 OK
+*/
+
+int myrg_detach_children(MYRG_INFO *m_info)
+{
+ DBUG_ENTER("myrg_detach_children");
+ /* For symmetry with myrg_attach_children() we use the mutex here. */
+ pthread_mutex_lock(&m_info->mutex);
+ if (m_info->tables)
+ {
+ /* Do not attach/detach an empty child list. */
+ m_info->children_attached= FALSE;
+ bzero((char*) m_info->open_tables, m_info->tables * sizeof(MYRG_TABLE));
+ }
+ m_info->records= 0;
+ m_info->del= 0;
+ m_info->data_file_length= 0;
+ m_info->options= 0;
+ pthread_mutex_unlock(&m_info->mutex);
+ DBUG_RETURN(0);
+}
+
diff --git a/storage/ndb/include/kernel/AttributeHeader.hpp b/storage/ndb/include/kernel/AttributeHeader.hpp
index 613e3d19d1b..52f93b6cd05 100644
--- a/storage/ndb/include/kernel/AttributeHeader.hpp
+++ b/storage/ndb/include/kernel/AttributeHeader.hpp
@@ -52,8 +52,7 @@ public:
// NOTE: in 5.1 ctors and init take size in bytes
/** Initialize AttributeHeader at location aHeaderPtr */
- static AttributeHeader& init(void* aHeaderPtr, Uint32 anAttributeId,
- Uint32 aByteSize);
+ static void init(Uint32* aHeaderPtr, Uint32 anAttributeId, Uint32 aByteSize);
/** Returns size of AttributeHeader (usually one or two words) */
Uint32 getHeaderSize() const; // In 32-bit words
@@ -113,10 +112,11 @@ public:
*/
inline
-AttributeHeader& AttributeHeader::init(void* aHeaderPtr, Uint32 anAttributeId,
- Uint32 aByteSize)
+void AttributeHeader::init(Uint32* aHeaderPtr, Uint32 anAttributeId,
+ Uint32 aByteSize)
{
- return * new (aHeaderPtr) AttributeHeader(anAttributeId, aByteSize);
+ AttributeHeader ah(anAttributeId, aByteSize);
+ *aHeaderPtr = ah.m_value;
}
inline
diff --git a/storage/ndb/include/kernel/GlobalSignalNumbers.h b/storage/ndb/include/kernel/GlobalSignalNumbers.h
index aa0596f102a..9653c20260f 100644
--- a/storage/ndb/include/kernel/GlobalSignalNumbers.h
+++ b/storage/ndb/include/kernel/GlobalSignalNumbers.h
@@ -195,9 +195,11 @@ extern const GlobalSignalNumber NO_OF_SIGNAL_NAMES;
/* 132 not unused */
/* 133 not unused */
#define GSN_CM_HEARTBEAT 134 /* distr. */
-/* 135 unused */
-/* 136 unused */
-/* 137 unused */
+
+#define GSN_PREPARE_COPY_FRAG_REQ 135
+#define GSN_PREPARE_COPY_FRAG_REF 136
+#define GSN_PREPARE_COPY_FRAG_CONF 137
+
#define GSN_CM_NODEINFOCONF 138 /* distr. */
#define GSN_CM_NODEINFOREF 139 /* distr. */
#define GSN_CM_NODEINFOREQ 140 /* distr. */
diff --git a/storage/ndb/include/kernel/signaldata/AccScan.hpp b/storage/ndb/include/kernel/signaldata/AccScan.hpp
index 73d69825069..a0aa38c8d8e 100644
--- a/storage/ndb/include/kernel/signaldata/AccScan.hpp
+++ b/storage/ndb/include/kernel/signaldata/AccScan.hpp
@@ -49,6 +49,7 @@ private:
Uint32 savePointId;
Uint32 gci;
};
+ Uint32 maxPage;
/**
* Previously there where also a scan type
diff --git a/storage/ndb/include/kernel/signaldata/CopyFrag.hpp b/storage/ndb/include/kernel/signaldata/CopyFrag.hpp
index 06dd4070264..d985358dce4 100644
--- a/storage/ndb/include/kernel/signaldata/CopyFrag.hpp
+++ b/storage/ndb/include/kernel/signaldata/CopyFrag.hpp
@@ -29,7 +29,7 @@ class CopyFragReq {
*/
friend class Dblqh;
public:
- STATIC_CONST( SignalLength = 9 );
+ STATIC_CONST( SignalLength = 10 );
private:
Uint32 userPtr;
@@ -42,6 +42,7 @@ private:
Uint32 gci;
Uint32 nodeCount;
Uint32 nodeList[1];
+ //Uint32 maxPage; is stored in nodeList[nodeCount]
};
class CopyFragConf {
@@ -95,4 +96,42 @@ struct UpdateFragDistKeyOrd
STATIC_CONST( SignalLength = 3 );
};
+struct PrepareCopyFragReq
+{
+ STATIC_CONST( SignalLength = 6 );
+
+ Uint32 senderRef;
+ Uint32 senderData;
+ Uint32 tableId;
+ Uint32 fragId;
+ Uint32 copyNodeId;
+ Uint32 startingNodeId;
+};
+
+struct PrepareCopyFragRef
+{
+ Uint32 senderRef;
+ Uint32 senderData;
+ Uint32 tableId;
+ Uint32 fragId;
+ Uint32 copyNodeId;
+ Uint32 startingNodeId;
+ Uint32 errorCode;
+
+ STATIC_CONST( SignalLength = 7 );
+};
+
+struct PrepareCopyFragConf
+{
+ STATIC_CONST( SignalLength = 7 );
+
+ Uint32 senderRef;
+ Uint32 senderData;
+ Uint32 tableId;
+ Uint32 fragId;
+ Uint32 copyNodeId;
+ Uint32 startingNodeId;
+ Uint32 maxPageNo;
+};
+
#endif
diff --git a/storage/ndb/include/kernel/signaldata/ScanTab.hpp b/storage/ndb/include/kernel/signaldata/ScanTab.hpp
index 0074078533f..3d2071ca019 100644
--- a/storage/ndb/include/kernel/signaldata/ScanTab.hpp
+++ b/storage/ndb/include/kernel/signaldata/ScanTab.hpp
@@ -46,6 +46,7 @@ public:
* Length of signal
*/
STATIC_CONST( StaticLength = 11 );
+ STATIC_CONST( MaxTotalAttrInfo = 0xFFFF );
private:
diff --git a/storage/ndb/include/kernel/signaldata/TcKeyConf.hpp b/storage/ndb/include/kernel/signaldata/TcKeyConf.hpp
index b8562875ef5..fd8932c3c87 100644
--- a/storage/ndb/include/kernel/signaldata/TcKeyConf.hpp
+++ b/storage/ndb/include/kernel/signaldata/TcKeyConf.hpp
@@ -46,7 +46,7 @@ public:
*/
STATIC_CONST( StaticLength = 5 );
STATIC_CONST( OperationLength = 2 );
- STATIC_CONST( SimpleReadBit = (((Uint32)1) << 31) );
+ STATIC_CONST( DirtyReadBit = (((Uint32)1) << 31) );
private:
diff --git a/storage/ndb/include/kernel/signaldata/TcKeyRef.hpp b/storage/ndb/include/kernel/signaldata/TcKeyRef.hpp
index 2846ce3854f..56f6cdae29d 100644
--- a/storage/ndb/include/kernel/signaldata/TcKeyRef.hpp
+++ b/storage/ndb/include/kernel/signaldata/TcKeyRef.hpp
@@ -40,12 +40,13 @@ class TcKeyRef {
friend bool printTCKEYREF(FILE *, const Uint32 *, Uint32, Uint16);
public:
- STATIC_CONST( SignalLength = 4 );
+ STATIC_CONST( SignalLength = 5 );
private:
Uint32 connectPtr;
Uint32 transId[2];
Uint32 errorCode;
+ Uint32 errorData;
};
#endif
diff --git a/storage/ndb/include/kernel/signaldata/TcRollbackRep.hpp b/storage/ndb/include/kernel/signaldata/TcRollbackRep.hpp
index 3b5e2f3d3cb..609756605d5 100644
--- a/storage/ndb/include/kernel/signaldata/TcRollbackRep.hpp
+++ b/storage/ndb/include/kernel/signaldata/TcRollbackRep.hpp
@@ -38,12 +38,13 @@ class TcRollbackRep {
friend bool printTCROLBACKREP(FILE *, const Uint32 *, Uint32, Uint16);
public:
- STATIC_CONST( SignalLength = 4 );
+ STATIC_CONST( SignalLength = 5 );
private:
Uint32 connectPtr;
Uint32 transId[2];
Uint32 returnCode;
+ Uint32 errorData;
};
#endif
diff --git a/storage/ndb/include/ndb_version.h.in b/storage/ndb/include/ndb_version.h.in
index 5405ad4d7aa..6a479433b3b 100644
--- a/storage/ndb/include/ndb_version.h.in
+++ b/storage/ndb/include/ndb_version.h.in
@@ -88,5 +88,52 @@ Uint32 ndbGetOwnVersion();
#define NDBD_NODE_VERSION_REP NDB_MAKE_VERSION(6,1,1)
+#define NDBD_PREPARE_COPY_FRAG_VERSION NDB_MAKE_VERSION(6,2,1)
+#define NDBD_PREPARE_COPY_FRAG_V2_51 NDB_MAKE_VERSION(5,1,23)
+#define NDBD_PREPARE_COPY_FRAG_V2_62 NDB_MAKE_VERSION(6,2,8)
+#define NDBD_PREPARE_COPY_FRAG_V2_63 NDB_MAKE_VERSION(6,3,6)
+
+/**
+ * 0 = NO PREP COPY FRAG SUPPORT
+ * 1 = NO MAX PAGE SUPPORT
+ * 2 = LATEST VERSION
+ */
+static
+inline
+int
+ndb_check_prep_copy_frag_version(Uint32 version)
+{
+ if (version == NDB_VERSION_D)
+ return 2;
+
+ const Uint32 major = (version >> 16) & 0xFF;
+ const Uint32 minor = (version >> 8) & 0xFF;
+ if (major >= 6)
+ {
+ if (minor == 2)
+ {
+ if (version >= NDBD_PREPARE_COPY_FRAG_V2_62)
+ return 2;
+ if (version >= NDBD_PREPARE_COPY_FRAG_VERSION)
+ return 1;
+ return 0;
+ }
+ else if (minor == 3)
+ {
+ if (version >= NDBD_PREPARE_COPY_FRAG_V2_63)
+ return 2;
+ return 1;
+ }
+ return 2;
+ }
+ else if (major == 5 && minor == 1)
+ {
+ if (version >= NDBD_PREPARE_COPY_FRAG_V2_51)
+ return 2;
+ }
+
+ return 0;
+}
+
#endif
diff --git a/storage/ndb/include/ndbapi/Ndb.hpp b/storage/ndb/include/ndbapi/Ndb.hpp
index 4d0219d1a3c..dca19c2ead0 100644
--- a/storage/ndb/include/ndbapi/Ndb.hpp
+++ b/storage/ndb/include/ndbapi/Ndb.hpp
@@ -17,7 +17,7 @@
@mainpage NDB API Programmers' Guide
This guide assumes a basic familiarity with MySQL Cluster concepts found
- on http://dev.mysql.com/doc/mysql/en/NDBCluster.html .
+ on http://dev.mysql.com/doc/mysql/en/mysql-cluster.html.
Some of the fundamental ones are also described in section @ref secConcepts.
The NDB API is a MySQL Cluster application interface
@@ -1056,6 +1056,7 @@ class Ndb
friend class NdbBlob;
friend class NdbImpl;
friend class Ndb_internal;
+ friend class NdbScanFilterImpl;
#endif
public:
diff --git a/storage/ndb/include/ndbapi/NdbDictionary.hpp b/storage/ndb/include/ndbapi/NdbDictionary.hpp
index 58882e139fd..0e782ba9214 100644
--- a/storage/ndb/include/ndbapi/NdbDictionary.hpp
+++ b/storage/ndb/include/ndbapi/NdbDictionary.hpp
@@ -1020,7 +1020,7 @@ public:
* Get the name of the table being indexed
*/
const char * getTable() const;
-
+
/**
* Get the number of columns in the index
*/
diff --git a/storage/ndb/include/ndbapi/NdbOperation.hpp b/storage/ndb/include/ndbapi/NdbOperation.hpp
index 06111941df4..78dbadfd7ab 100644
--- a/storage/ndb/include/ndbapi/NdbOperation.hpp
+++ b/storage/ndb/include/ndbapi/NdbOperation.hpp
@@ -93,8 +93,9 @@ public:
,LM_CommittedRead ///< Ignore locks, read last committed value
#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
= 2,
- LM_Dirty = 2
+ LM_Dirty = 2,
#endif
+ LM_SimpleRead = 3 ///< Read with shared lock, but release lock directly
};
/**
@@ -842,8 +843,10 @@ protected:
virtual ~NdbOperation();
void next(NdbOperation*); // Set next pointer
NdbOperation* next(); // Get next pointer
+
public:
#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
+ NdbTransaction* getNdbTransaction();
const NdbOperation* next() const;
const NdbRecAttr* getFirstRecAttr() const;
#endif
diff --git a/storage/ndb/include/ndbapi/NdbPool.hpp b/storage/ndb/include/ndbapi/NdbPool.hpp
index 1963bf26448..44b6d7488f0 100644
--- a/storage/ndb/include/ndbapi/NdbPool.hpp
+++ b/storage/ndb/include/ndbapi/NdbPool.hpp
@@ -17,7 +17,8 @@ class Ndb;
class NdbPool;
bool
-create_instance(Uint32 max_ndb_objects,
+create_instance(Ndb_cluster_connection* cc,
+ Uint32 max_ndb_objects,
Uint32 no_conn_obj,
Uint32 init_no_ndb_objects);
diff --git a/storage/ndb/include/ndbapi/NdbScanFilter.hpp b/storage/ndb/include/ndbapi/NdbScanFilter.hpp
index 1ef62558560..4527012a6c4 100644
--- a/storage/ndb/include/ndbapi/NdbScanFilter.hpp
+++ b/storage/ndb/include/ndbapi/NdbScanFilter.hpp
@@ -17,6 +17,7 @@
#define NDB_SCAN_FILTER_HPP
#include <ndb_types.h>
+#include <ndbapi_limits.h>
/**
* @class NdbScanFilter
@@ -31,8 +32,13 @@ public:
/**
* Constructor
* @param op The NdbOperation that the filter belongs to (is applied to).
+ * @param abort_on_too_large abort transaction on filter too large
+ * default: true
+ * @param max_size Maximum size of generated filter in words
*/
- NdbScanFilter(class NdbOperation * op);
+ NdbScanFilter(class NdbOperation * op,
+ bool abort_on_too_large = true,
+ Uint32 max_size = NDB_MAX_SCANFILTER_SIZE_IN_WORDS);
~NdbScanFilter();
/**
@@ -166,6 +172,27 @@ public:
/** @} *********************************************************************/
#endif
+ enum Error {
+ FilterTooLarge = 4294
+ };
+
+ /**
+ * Get filter level error.
+ *
+ * Most errors are set only on operation level, and they abort the
+ * transaction. The error FilterTooLarge is set on filter level and
+ * by default it propagates to operation level and also aborts the
+ * transaction.
+ *
+ * If option abort_on_too_large is set to false, then FilterTooLarge
+ * does not propagate. One can then either ignore this error (in
+ * which case no filtering is done) or try to define a new filter
+ * immediately.
+ */
+ const class NdbError & getNdbError() const;
+#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
+ NdbOperation * getNdbOperation();
+#endif
private:
#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
friend class NdbScanFilterImpl;
diff --git a/storage/ndb/include/ndbapi/NdbTransaction.hpp b/storage/ndb/include/ndbapi/NdbTransaction.hpp
index 20c9c709e51..6a057655398 100644
--- a/storage/ndb/include/ndbapi/NdbTransaction.hpp
+++ b/storage/ndb/include/ndbapi/NdbTransaction.hpp
@@ -170,6 +170,15 @@ public:
#endif
};
+#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
+ /**
+ * Convenience method to fetch this transaction's Ndb* object
+ */
+ Ndb * getNdb() {
+ return theNdb;
+ }
+#endif
+
#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED
/**
* Get an NdbOperation for a table.
diff --git a/storage/ndb/include/ndbapi/ndbapi_limits.h b/storage/ndb/include/ndbapi/ndbapi_limits.h
index 63399e4bd0a..e283913d059 100644
--- a/storage/ndb/include/ndbapi/ndbapi_limits.h
+++ b/storage/ndb/include/ndbapi/ndbapi_limits.h
@@ -26,4 +26,6 @@
#define NDB_MAX_TUPLE_SIZE (NDB_MAX_TUPLE_SIZE_IN_WORDS*4)
#define NDB_MAX_ACTIVE_EVENTS 100
+#define NDB_MAX_SCANFILTER_SIZE_IN_WORDS 50000
+
#endif
diff --git a/storage/ndb/include/portlib/NdbThread.h b/storage/ndb/include/portlib/NdbThread.h
index 569048cf6c8..373e2218c6c 100644
--- a/storage/ndb/include/portlib/NdbThread.h
+++ b/storage/ndb/include/portlib/NdbThread.h
@@ -41,7 +41,7 @@ struct NdbThread;
signum set in g_ndb_shm_signum in a portable manner.
*/
#ifdef NDB_SHM_TRANSPORTER
-void NdbThread_set_shm_sigmask(bool block);
+void NdbThread_set_shm_sigmask(my_bool block);
#endif
/**
diff --git a/storage/ndb/include/util/ndb_rand.h b/storage/ndb/include/util/ndb_rand.h
new file mode 100644
index 00000000000..1521ca9c4ff
--- /dev/null
+++ b/storage/ndb/include/util/ndb_rand.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef NDB_RAND_H
+#define NDB_RAND_H
+
+#define NDB_RAND_MAX 32767
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ndb_rand(void);
+
+void ndb_srand(unsigned seed);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/storage/ndb/src/common/debugger/EventLogger.cpp b/storage/ndb/src/common/debugger/EventLogger.cpp
index 0964a54f906..068b0c6ac18 100644
--- a/storage/ndb/src/common/debugger/EventLogger.cpp
+++ b/storage/ndb/src/common/debugger/EventLogger.cpp
@@ -498,10 +498,10 @@ void getTextTransReportCounters(QQQQ) {
// -------------------------------------------------------------------
BaseString::snprintf(m_text, m_text_len,
"Trans. Count = %u, Commit Count = %u, "
- "Read Count = %u, Simple Read Count = %u,\n"
+ "Read Count = %u, Simple Read Count = %u, "
"Write Count = %u, AttrInfo Count = %u, "
- "Concurrent Operations = %u, Abort Count = %u\n"
- " Scans: %u Range scans: %u",
+ "Concurrent Operations = %u, Abort Count = %u"
+ " Scans = %u Range scans = %u",
theData[1],
theData[2],
theData[3],
@@ -797,9 +797,9 @@ void getTextBackupFailedToStart(QQQQ) {
}
void getTextBackupCompleted(QQQQ) {
BaseString::snprintf(m_text, m_text_len,
- "Backup %u started from node %u completed\n"
- " StartGCP: %u StopGCP: %u\n"
- " #Records: %u #LogRecords: %u\n"
+ "Backup %u started from node %u completed."
+ " StartGCP: %u StopGCP: %u"
+ " #Records: %u #LogRecords: %u"
" Data: %u bytes Log: %u bytes",
theData[2], refToNode(theData[1]),
theData[3], theData[4], theData[6], theData[8],
diff --git a/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp b/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp
index 0d31cd5de7f..b4221cbec8e 100644
--- a/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp
+++ b/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp
@@ -640,5 +640,9 @@ const GsnName SignalNames [] = {
,{ GSN_ROUTE_ORD, "ROUTE_ORD" }
,{ GSN_NODE_VERSION_REP, "NODE_VERSION_REP" }
+
+ ,{ GSN_PREPARE_COPY_FRAG_REQ, "PREPARE_COPY_FRAG_REQ" }
+ ,{ GSN_PREPARE_COPY_FRAG_REF, "PREPARE_COPY_FRAG_REF" }
+ ,{ GSN_PREPARE_COPY_FRAG_CONF, "PREPARE_COPY_FRAG_CONF" }
};
const unsigned short NO_OF_SIGNAL_NAMES = sizeof(SignalNames)/sizeof(GsnName);
diff --git a/storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp b/storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp
index 65589f8cd6e..377863f9446 100644
--- a/storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp
+++ b/storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp
@@ -51,11 +51,11 @@ printTCKEYCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receive
(TcKeyConf::getMarkerFlag(confInfo) == 0)?"false":"true");
fprintf(output, "Operations:\n");
for(i = 0; i < noOfOp; i++) {
- if(sig->operations[i].attrInfoLen > TcKeyConf::SimpleReadBit)
+ if(sig->operations[i].attrInfoLen > TcKeyConf::DirtyReadBit)
fprintf(output,
" apiOperationPtr: H'%.8x, simplereadnode: %u\n",
sig->operations[i].apiOperationPtr,
- sig->operations[i].attrInfoLen & (~TcKeyConf::SimpleReadBit));
+ sig->operations[i].attrInfoLen & (~TcKeyConf::DirtyReadBit));
else
fprintf(output,
" apiOperationPtr: H'%.8x, attrInfoLen: %u\n",
diff --git a/storage/ndb/src/common/transporter/Transporter.cpp b/storage/ndb/src/common/transporter/Transporter.cpp
index cec018575e0..269a5fba4e9 100644
--- a/storage/ndb/src/common/transporter/Transporter.cpp
+++ b/storage/ndb/src/common/transporter/Transporter.cpp
@@ -70,7 +70,7 @@ Transporter::Transporter(TransporterRegistry &t_reg,
signalIdUsed = _signalId;
m_connected = false;
- m_timeOutMillis = 1000;
+ m_timeOutMillis = 30000;
m_connect_address.s_addr= 0;
if(s_port<0)
@@ -101,7 +101,7 @@ Transporter::connect_server(NDB_SOCKET_TYPE sockfd) {
if(m_connected)
{
- DBUG_RETURN(true); // TODO assert(0);
+ DBUG_RETURN(false); // TODO assert(0);
}
{
diff --git a/storage/ndb/src/common/transporter/TransporterRegistry.cpp b/storage/ndb/src/common/transporter/TransporterRegistry.cpp
index 5f5f3c17b2d..848738b2983 100644
--- a/storage/ndb/src/common/transporter/TransporterRegistry.cpp
+++ b/storage/ndb/src/common/transporter/TransporterRegistry.cpp
@@ -758,7 +758,8 @@ TransporterRegistry::poll_TCP(Uint32 timeOutMillis)
TCP_Transporter * t = theTCPTransporters[i];
// If the transporter is connected
- if (t->isConnected()) {
+ NodeId nodeId = t->getRemoteNodeId();
+ if (is_connected(nodeId) && t->isConnected()) {
const NDB_SOCKET_TYPE socket = t->getSocket();
// Find the highest socket value. It will be used by select
diff --git a/storage/ndb/src/common/util/Makefile.am b/storage/ndb/src/common/util/Makefile.am
index d331cce7e5c..5379a425c49 100644
--- a/storage/ndb/src/common/util/Makefile.am
+++ b/storage/ndb/src/common/util/Makefile.am
@@ -24,7 +24,8 @@ libgeneral_la_SOURCES = \
uucode.c random.c version.c \
strdup.c \
ConfigValues.cpp ndb_init.c basestring_vsnprintf.c \
- Bitmask.cpp
+ Bitmask.cpp \
+ ndb_rand.c
EXTRA_PROGRAMS = testBitmask
testBitmask_SOURCES = testBitmask.cpp
diff --git a/storage/ndb/src/common/util/ndb_rand.c b/storage/ndb/src/common/util/ndb_rand.c
new file mode 100644
index 00000000000..4fcc483cd49
--- /dev/null
+++ b/storage/ndb/src/common/util/ndb_rand.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_rand.h>
+
+static unsigned long next= 1;
+
+/**
+ * ndb_rand
+ *
+ * constant time, cheap, pseudo-random number generator.
+ *
+ * NDB_RAND_MAX assumed to be 32767
+ *
+ * This is the POSIX example for "generating the same sequence on
+ * different machines". Although that is not one of our requirements.
+ */
+int ndb_rand(void)
+{
+ next= next * 1103515245 + 12345;
+ return((unsigned)(next/65536) % 32768);
+}
+
+void ndb_srand(unsigned seed)
+{
+ next= seed;
+}
+
diff --git a/storage/ndb/src/cw/cpcd/APIService.cpp b/storage/ndb/src/cw/cpcd/APIService.cpp
index 1c1cfb94cd4..f60abc08817 100644
--- a/storage/ndb/src/cw/cpcd/APIService.cpp
+++ b/storage/ndb/src/cw/cpcd/APIService.cpp
@@ -145,7 +145,7 @@ CPCDAPISession::CPCDAPISession(NDB_SOCKET_TYPE sock,
: SocketServer::Session(sock)
, m_cpcd(cpcd)
{
- m_input = new SocketInputStream(sock);
+ m_input = new SocketInputStream(sock, 7*24*60*60000);
m_output = new SocketOutputStream(sock);
m_parser = new Parser<CPCDAPISession>(commands, *m_input, true, true, true);
}
diff --git a/storage/ndb/src/kernel/blocks/ERROR_codes.txt b/storage/ndb/src/kernel/blocks/ERROR_codes.txt
index 5ed4f5b0663..9462c12c709 100644
--- a/storage/ndb/src/kernel/blocks/ERROR_codes.txt
+++ b/storage/ndb/src/kernel/blocks/ERROR_codes.txt
@@ -3,17 +3,17 @@ Next NDBCNTR 1002
Next NDBFS 2000
Next DBACC 3002
Next DBTUP 4029
-Next DBLQH 5045
+Next DBLQH 5047
Next DBDICT 6008
-Next DBDIH 7186
-Next DBTC 8053
+Next DBDIH 7195
+Next DBTC 8054
Next CMVMI 9000
Next BACKUP 10038
Next DBUTIL 11002
Next DBTUX 12008
-Next SUMA 13001
Next LGMAN 15001
Next TSMAN 16001
+Next SUMA 13034
TESTING NODE FAILURE, ARBITRATION
---------------------------------
@@ -83,6 +83,11 @@ Delay GCP_SAVEREQ by 10 secs
7185: Dont reply to COPY_GCI_REQ where reason == GCP
+7193: Dont send LCP_FRAG_ORD to self, and crash when sending first
+ LCP_FRAG_ORD(last)
+
+7194: Force removeNodeFromStored to complete in the middle of MASTER_LCPCONF
+
ERROR CODES FOR TESTING NODE FAILURE, LOCAL CHECKPOINT HANDLING:
-----------------------------------------------------------------
@@ -159,6 +164,9 @@ And crash when all have "not" been sent
7027: Crash in master when changing state to LCP_TAB_SAVED
7018: Crash in master when changing state to LCP_TAB_SAVED
+7191: Crash when receiving LCP_COMPLETE_REP
+7192: Crash in setLcpActiveStatusStart - when dead node missed to LCP's
+
ERROR CODES FOR TESTING NODE FAILURE, FAILURE IN COPY FRAGMENT PROCESS:
-----------------------------------------------------------------------
@@ -185,6 +193,8 @@ handling in DBTC to ensure that node failures are also well handled in
time-out handling. They can also be used to test multiple node failure
handling.
+5045: Crash in PREPARE_COPY_FRAG_REQ
+5046: Crash if LQHKEYREQ (NrCopy) comes when frag-state is incorrect
ERROR CODES FOR TESTING TIME-OUT HANDLING IN DBLQH
-------------------------------------------------
@@ -250,6 +260,8 @@ Delay execution of ABORTCONF signal 2 seconds to generate time-out.
8050: Send ZABORT_TIMEOUT_BREAK delayed
+8053: Crash in timeOutFoundLab, state CS_WAIT_COMMIT_CONF
+
ERROR CODES FOR TESTING TIME-OUT HANDLING IN DBTC
-------------------------------------------------
diff --git a/storage/ndb/src/kernel/blocks/backup/Backup.cpp b/storage/ndb/src/kernel/blocks/backup/Backup.cpp
index fb234c2afef..ec7b3f42374 100644
--- a/storage/ndb/src/kernel/blocks/backup/Backup.cpp
+++ b/storage/ndb/src/kernel/blocks/backup/Backup.cpp
@@ -1027,8 +1027,9 @@ Backup::execINCL_NODEREQ(Signal* signal)
break;
}//if
}//for
- signal->theData[0] = reference();
- sendSignal(senderRef, GSN_INCL_NODECONF, signal, 1, JBB);
+ signal->theData[0] = inclNode;
+ signal->theData[1] = reference();
+ sendSignal(senderRef, GSN_INCL_NODECONF, signal, 2, JBB);
}
/*****************************************************************************
diff --git a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
index 7a992587010..6c869435bfa 100644
--- a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
+++ b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
@@ -421,9 +421,10 @@ void Cmvmi::execCLOSE_COMREQ(Signal* signal)
// Uint32 noOfNodes = closeCom->noOfNodes;
jamEntry();
- for (unsigned i = 0; i < MAX_NODES; i++){
- if(NodeBitmask::get(closeCom->theNodes, i)){
-
+ for (unsigned i = 0; i < MAX_NODES; i++)
+ {
+ if(NodeBitmask::get(closeCom->theNodes, i))
+ {
jam();
//-----------------------------------------------------
@@ -437,7 +438,9 @@ void Cmvmi::execCLOSE_COMREQ(Signal* signal)
globalTransporterRegistry.do_disconnect(i);
}
}
- if (failNo != 0) {
+
+ if (failNo != 0)
+ {
jam();
signal->theData[0] = userRef;
signal->theData[1] = failNo;
@@ -456,13 +459,21 @@ void Cmvmi::execOPEN_COMREQ(Signal* signal)
jamEntry();
const Uint32 len = signal->getLength();
- if(len == 2){
-
+ if(len == 2)
+ {
#ifdef ERROR_INSERT
if (! ((ERROR_INSERTED(9000) || ERROR_INSERTED(9002))
&& c_error_9000_nodes_mask.get(tStartingNode)))
#endif
{
+ if (globalData.theStartLevel != NodeState::SL_STARTED &&
+ (getNodeInfo(tStartingNode).m_type != NodeInfo::DB &&
+ getNodeInfo(tStartingNode).m_type != NodeInfo::MGM))
+ {
+ jam();
+ goto done;
+ }
+
globalTransporterRegistry.do_connect(tStartingNode);
globalTransporterRegistry.setIOState(tStartingNode, HaltIO);
@@ -475,9 +486,11 @@ void Cmvmi::execOPEN_COMREQ(Signal* signal)
//-----------------------------------------------------
}
} else {
- for(unsigned int i = 1; i < MAX_NODES; i++ ) {
+ for(unsigned int i = 1; i < MAX_NODES; i++ )
+ {
jam();
- if (i != getOwnNodeId() && getNodeInfo(i).m_type == tData2){
+ if (i != getOwnNodeId() && getNodeInfo(i).m_type == tData2)
+ {
jam();
#ifdef ERROR_INSERT
@@ -496,6 +509,7 @@ void Cmvmi::execOPEN_COMREQ(Signal* signal)
}
}
+done:
if (userRef != 0) {
jam();
signal->theData[0] = tStartingNode;
@@ -536,24 +550,10 @@ void Cmvmi::execDISCONNECT_REP(Signal *signal)
setNodeInfo(hostId).m_connectCount++;
const NodeInfo::NodeType type = getNodeInfo(hostId).getType();
ndbrequire(type != NodeInfo::INVALID);
-
- if(type == NodeInfo::DB || globalData.theStartLevel == NodeState::SL_STARTED){
- jam();
- DisconnectRep * const rep = (DisconnectRep *)&signal->theData[0];
- rep->nodeId = hostId;
- rep->err = errNo;
- sendSignal(QMGR_REF, GSN_DISCONNECT_REP, signal,
- DisconnectRep::SignalLength, JBA);
- } else if((globalData.theStartLevel == NodeState::SL_CMVMI ||
- globalData.theStartLevel == NodeState::SL_STARTING)
- && type == NodeInfo::MGM) {
- /**
- * Someone disconnected during cmvmi period
- */
- jam();
- globalTransporterRegistry.do_connect(hostId);
- }
+ sendSignal(QMGR_REF, GSN_DISCONNECT_REP, signal,
+ DisconnectRep::SignalLength, JBA);
+
cancelSubscription(hostId);
signal->theData[0] = NDB_LE_Disconnected;
@@ -587,6 +587,8 @@ void Cmvmi::execCONNECT_REP(Signal *signal){
*/
if(type == NodeInfo::MGM){
jam();
+ signal->theData[0] = hostId;
+ sendSignal(QMGR_REF, GSN_CONNECT_REP, signal, 1, JBA);
} else {
/**
* Dont allow api nodes to connect
@@ -802,6 +804,8 @@ Cmvmi::execSTART_ORD(Signal* signal) {
}
}
}
+
+ EXECUTE_DIRECT(QMGR, GSN_START_ORD, signal, 1);
return ;
}
@@ -829,9 +833,6 @@ Cmvmi::execSTART_ORD(Signal* signal) {
*
* Do Restart
*/
-
- globalScheduler.clear();
- globalTimeQueue.clear();
// Disconnect all nodes as part of the system restart.
// We need to ensure that we are starting up
@@ -1124,6 +1125,38 @@ Cmvmi::execDUMP_STATE_ORD(Signal* signal)
}
#endif
#endif
+
+ if (arg == 9999)
+ {
+ Uint32 delay = 1000;
+ switch(signal->getLength()){
+ case 1:
+ break;
+ case 2:
+ delay = signal->theData[1];
+ break;
+ default:{
+ Uint32 dmin = signal->theData[1];
+ Uint32 dmax = signal->theData[2];
+ delay = dmin + (rand() % (dmax - dmin));
+ break;
+ }
+ }
+
+ signal->theData[0] = 9999;
+ if (delay == 0)
+ {
+ execNDB_TAMPER(signal);
+ }
+ else if (delay < 10)
+ {
+ sendSignal(reference(), GSN_NDB_TAMPER, signal, 1, JBB);
+ }
+ else
+ {
+ sendSignalWithDelay(reference(), GSN_NDB_TAMPER, signal, delay, 1);
+ }
+ }
}//Cmvmi::execDUMP_STATE_ORD()
void
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
index 569958a6aa9..a61a5bc035c 100644
--- a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
@@ -3825,8 +3825,9 @@ void Dbdict::execINCL_NODEREQ(Signal* signal)
c_nodes.getPtr(nodePtr);
ndbrequire(nodePtr.p->nodeState == NodeRecord::NDB_NODE_DEAD);
nodePtr.p->nodeState = NodeRecord::NDB_NODE_ALIVE;
- signal->theData[0] = reference();
- sendSignal(retRef, GSN_INCL_NODECONF, signal, 1, JBB);
+ signal->theData[0] = nodePtr.i;
+ signal->theData[1] = reference();
+ sendSignal(retRef, GSN_INCL_NODECONF, signal, 2, JBB);
c_aliveNodes.set(nodePtr.i);
}//execINCL_NODEREQ()
@@ -4085,9 +4086,7 @@ Dbdict::execALTER_TABLE_REQ(Signal* signal)
bool ok = false;
switch(tabState){
case TableRecord::NOT_DEFINED:
- case TableRecord::REORG_TABLE_PREPARED:
case TableRecord::DEFINING:
- case TableRecord::CHECKED:
jam();
alterTableRef(signal, req, AlterTableRef::NoSuchTable);
return;
@@ -4338,9 +4337,7 @@ Dbdict::execALTER_TAB_REQ(Signal * signal)
bool ok = false;
switch(tabState){
case TableRecord::NOT_DEFINED:
- case TableRecord::REORG_TABLE_PREPARED:
case TableRecord::DEFINING:
- case TableRecord::CHECKED:
jam();
alterTabRef(signal, req, AlterTableRef::NoSuchTable);
return;
@@ -6689,9 +6686,7 @@ Dbdict::execDROP_TABLE_REQ(Signal* signal){
bool ok = false;
switch(tabState){
case TableRecord::NOT_DEFINED:
- case TableRecord::REORG_TABLE_PREPARED:
case TableRecord::DEFINING:
- case TableRecord::CHECKED:
jam();
dropTableRef(signal, req, DropTableRef::NoSuchTable);
return;
@@ -7717,7 +7712,6 @@ Dbdict::execLIST_TABLES_REQ(Signal* signal)
if(DictTabInfo::isTable(type)){
switch (tablePtr.p->tabState) {
case TableRecord::DEFINING:
- case TableRecord::CHECKED:
conf->setTableState(pos, DictTabInfo::StateBuilding);
break;
case TableRecord::PREPARE_DROPPING:
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
index 3fff330d699..1189b23c14d 100644
--- a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
@@ -320,9 +320,7 @@ public:
enum TabState {
NOT_DEFINED = 0,
- REORG_TABLE_PREPARED = 1,
DEFINING = 2,
- CHECKED = 3,
DEFINED = 4,
PREPARE_DROPPING = 5,
DROPPING = 6,
diff --git a/storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp b/storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
index 5bef13cd0b9..b0bbdefff55 100644
--- a/storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
+++ b/storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
@@ -318,6 +318,7 @@ public:
Uint8 noOfStartedChkpt;
MasterLCPConf::State lcpStateAtTakeOver;
+ Uint32 m_remove_node_from_table_lcp_id;
};
typedef Ptr<NodeRecord> NodeRecordPtr;
/**********************************************************************/
@@ -544,7 +545,8 @@ public:
TO_WAIT_ENDING = 21,
ENDING = 22,
- STARTING_LOCAL_FRAGMENTS = 24
+ STARTING_LOCAL_FRAGMENTS = 24,
+ PREPARE_COPY = 25
};
enum ToSlaveStatus {
TO_SLAVE_IDLE = 0,
@@ -555,6 +557,7 @@ public:
TO_SLAVE_COPY_COMPLETED = 5
};
Uint32 startGci;
+ Uint32 maxPage;
Uint32 toCopyNode;
Uint32 toCurrentFragid;
Uint32 toCurrentReplica;
@@ -671,6 +674,8 @@ private:
void execNODE_FAILREP(Signal *);
void execCOPY_FRAGCONF(Signal *);
void execCOPY_FRAGREF(Signal *);
+ void execPREPARE_COPY_FRAG_REF(Signal*);
+ void execPREPARE_COPY_FRAG_CONF(Signal*);
void execDIADDTABREQ(Signal *);
void execDIGETNODESREQ(Signal *);
void execDIRELEASEREQ(Signal *);
@@ -1113,6 +1118,7 @@ private:
void sendStartTo(Signal *, Uint32 takeOverPtr);
void startNextCopyFragment(Signal *, Uint32 takeOverPtr);
void toCopyFragLab(Signal *, Uint32 takeOverPtr);
+ void toStartCopyFrag(Signal *, TakeOverRecordPtr);
void startHsAddFragConfLab(Signal *);
void prepareSendCreateFragReq(Signal *, Uint32 takeOverPtr);
void sendUpdateTo(Signal *, Uint32 takeOverPtr, Uint32 updateState);
@@ -1304,7 +1310,17 @@ private:
LcpStatus lcpStatus;
Uint32 lcpStatusUpdatedPlace;
+ struct Save {
+ LcpStatus m_status;
+ Uint32 m_place;
+ } m_saveState[10];
+
void setLcpStatus(LcpStatus status, Uint32 line){
+ for (Uint32 i = 9; i > 0; i--)
+ m_saveState[i] = m_saveState[i-1];
+ m_saveState[0].m_status = lcpStatus;
+ m_saveState[0].m_place = lcpStatusUpdatedPlace;
+
lcpStatus = status;
lcpStatusUpdatedPlace = line;
}
diff --git a/storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp b/storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp
index aff31d625f4..6ce281434c2 100644
--- a/storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp
+++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp
@@ -259,6 +259,11 @@ Dbdih::Dbdih(Block_context& ctx):
addRecSignal(GSN_START_FRAGREF,
&Dbdih::execSTART_FRAGREF);
+
+ addRecSignal(GSN_PREPARE_COPY_FRAG_REF,
+ &Dbdih::execPREPARE_COPY_FRAG_REF);
+ addRecSignal(GSN_PREPARE_COPY_FRAG_CONF,
+ &Dbdih::execPREPARE_COPY_FRAG_CONF);
apiConnectRecord = 0;
connectRecord = 0;
diff --git a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
index d2d989ce5b9..e2b1058242d 100644
--- a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
+++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
@@ -2135,12 +2135,9 @@ void Dbdih::gcpBlockedLab(Signal* signal)
/*---------------------------------------------------------------------------*/
void Dbdih::execINCL_NODECONF(Signal* signal)
{
- Uint32 TsendNodeId;
- Uint32 TstartNode_or_blockref;
-
jamEntry();
- TstartNode_or_blockref = signal->theData[0];
- TsendNodeId = signal->theData[1];
+ Uint32 TstartNode = signal->theData[0];
+ Uint32 TsendNodeId_or_blockref = signal->theData[1];
Uint32 blocklist[6];
blocklist[0] = clocallqhblockref;
@@ -2152,9 +2149,21 @@ void Dbdih::execINCL_NODECONF(Signal* signal)
for (Uint32 i = 0; blocklist[i] != 0; i++)
{
- if (TstartNode_or_blockref == blocklist[i])
+ if (TsendNodeId_or_blockref == blocklist[i])
{
jam();
+
+ if (TstartNode != c_nodeStartSlave.nodeId)
+ {
+ jam();
+ warningEvent("Recevied INCL_NODECONF for %u from %s"
+ " while %u is starting",
+ TstartNode,
+ getBlockName(refToBlock(TsendNodeId_or_blockref)),
+ c_nodeStartSlave.nodeId);
+ return;
+ }
+
if (getNodeStatus(c_nodeStartSlave.nodeId) == NodeRecord::ALIVE &&
blocklist[i+1] != 0)
{
@@ -2182,10 +2191,21 @@ void Dbdih::execINCL_NODECONF(Signal* signal)
}
}
}
+
+ if (c_nodeStartMaster.startNode != TstartNode)
+ {
+ jam();
+ warningEvent("Recevied INCL_NODECONF for %u from %u"
+ " while %u is starting",
+ TstartNode,
+ TsendNodeId_or_blockref,
+ c_nodeStartMaster.startNode);
+ return;
+ }
ndbrequire(cmasterdihref = reference());
- receiveLoopMacro(INCL_NODEREQ, TsendNodeId);
-
+ receiveLoopMacro(INCL_NODEREQ, TsendNodeId_or_blockref);
+
CRASH_INSERTION(7128);
/*-------------------------------------------------------------------------*/
// Now that we have included the starting node in the node lists in the
@@ -3155,6 +3175,94 @@ void Dbdih::toCopyFragLab(Signal* signal,
TakeOverRecordPtr takeOverPtr;
RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ /**
+ * Inform starting node that TakeOver is about to start
+ */
+ Uint32 nodeId = takeOverPtr.p->toStartingNode;
+
+ Uint32 version = getNodeInfo(nodeId).m_version;
+ if (ndb_check_prep_copy_frag_version(version))
+ {
+ jam();
+ TabRecordPtr tabPtr;
+ tabPtr.i = takeOverPtr.p->toCurrentTabref;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, takeOverPtr.p->toCurrentFragid, fragPtr);
+ Uint32 nodes[MAX_REPLICAS];
+ extractNodeInfo(fragPtr.p, nodes);
+
+ PrepareCopyFragReq* req= (PrepareCopyFragReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = takeOverPtrI;
+ req->tableId = takeOverPtr.p->toCurrentTabref;
+ req->fragId = takeOverPtr.p->toCurrentFragid;
+ req->copyNodeId = nodes[0]; // Src
+ req->startingNodeId = takeOverPtr.p->toStartingNode; // Dst
+ Uint32 ref = calcLqhBlockRef(takeOverPtr.p->toStartingNode);
+
+ sendSignal(ref, GSN_PREPARE_COPY_FRAG_REQ, signal,
+ PrepareCopyFragReq::SignalLength, JBB);
+
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::PREPARE_COPY;
+ return;
+ }
+
+ takeOverPtr.p->maxPage = RNIL;
+ toStartCopyFrag(signal, takeOverPtr);
+}
+
+void
+Dbdih::execPREPARE_COPY_FRAG_REF(Signal* signal)
+{
+ jamEntry();
+ PrepareCopyFragRef ref = *(PrepareCopyFragRef*)signal->getDataPtr();
+
+ TakeOverRecordPtr takeOverPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(ref.senderData, takeOverPtr);
+
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::PREPARE_COPY);
+
+ /**
+ * Treat this as copy frag ref
+ */
+ CopyFragRef * cfref = (CopyFragRef*)signal->getDataPtrSend();
+ cfref->userPtr = ref.senderData;
+ cfref->startingNodeId = ref.startingNodeId;
+ cfref->errorCode = ref.errorCode;
+ cfref->tableId = ref.tableId;
+ cfref->fragId = ref.fragId;
+ cfref->sendingNodeId = ref.copyNodeId;
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::COPY_FRAG;
+ execCOPY_FRAGREF(signal);
+}
+
+void
+Dbdih::execPREPARE_COPY_FRAG_CONF(Signal* signal)
+{
+ PrepareCopyFragConf conf = *(PrepareCopyFragConf*)signal->getDataPtr();
+
+ TakeOverRecordPtr takeOverPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(conf.senderData, takeOverPtr);
+
+ Uint32 version = getNodeInfo(refToNode(conf.senderRef)).m_version;
+ if (ndb_check_prep_copy_frag_version(version) >= 2)
+ {
+ jam();
+ takeOverPtr.p->maxPage = conf.maxPageNo;
+ }
+ else
+ {
+ jam();
+ takeOverPtr.p->maxPage = RNIL;
+ }
+ toStartCopyFrag(signal, takeOverPtr);
+}
+
+void
+Dbdih::toStartCopyFrag(Signal* signal, TakeOverRecordPtr takeOverPtr)
+{
CreateReplicaRecordPtr createReplicaPtr;
createReplicaPtr.i = 0;
ptrAss(createReplicaPtr, createReplicaRecord);
@@ -3178,8 +3286,8 @@ void Dbdih::toCopyFragLab(Signal* signal,
createReplicaPtr.p->hotSpareUse = true;
createReplicaPtr.p->dataNodeId = takeOverPtr.p->toStartingNode;
- prepareSendCreateFragReq(signal, takeOverPtrI);
-}//Dbdih::toCopyFragLab()
+ prepareSendCreateFragReq(signal, takeOverPtr.i);
+}//Dbdih::toStartCopy()
void Dbdih::prepareSendCreateFragReq(Signal* signal, Uint32 takeOverPtrI)
{
@@ -3412,10 +3520,12 @@ void Dbdih::execCREATE_FRAGCONF(Signal* signal)
copyFragReq->schemaVersion = tabPtr.p->schemaVersion;
copyFragReq->distributionKey = fragPtr.p->distributionKey;
copyFragReq->gci = gci;
- copyFragReq->nodeCount = extractNodeInfo(fragPtr.p,
- copyFragReq->nodeList);
+ Uint32 len = copyFragReq->nodeCount =
+ extractNodeInfo(fragPtr.p,
+ copyFragReq->nodeList);
+ copyFragReq->nodeList[len] = takeOverPtr.p->maxPage;
sendSignal(ref, GSN_COPY_FRAGREQ, signal,
- CopyFragReq::SignalLength + copyFragReq->nodeCount, JBB);
+ CopyFragReq::SignalLength + len, JBB);
} else {
ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::COMMIT_CREATE);
jam();
@@ -4576,13 +4686,22 @@ void Dbdih::checkTakeOverInMasterStartNodeFailure(Signal* signal,
ok = true;
jam();
//-----------------------------------------------------------------------
- // The starting node will discover the problem. We will receive either
+ // The copying node will discover the problem. We will receive either
// COPY_FRAGREQ or COPY_FRAGCONF and then we can release the take over
// record and end the process. If the copying node should also die then
// we will try to send prepare create fragment and will then discover
// that the starting node has failed.
//-----------------------------------------------------------------------
break;
+ case TakeOverRecord::PREPARE_COPY:
+ ok = true;
+ jam();
+ /**
+ * We're waiting for the starting node...which just died...
+ * endTakeOver
+ */
+ endTakeOver(takeOverPtr.i);
+ break;
case TakeOverRecord::COPY_ACTIVE:
ok = true;
jam();
@@ -5069,12 +5188,32 @@ void Dbdih::startRemoveFailedNode(Signal* signal, NodeRecordPtr failedNodePtr)
return;
}
- jam();
- signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
- signal->theData[1] = failedNodePtr.i;
- signal->theData[2] = 0; // Tab id
- sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ /**
+ * If node has node complete LCP
+ * we need to remove it as undo might not be complete
+ * bug#31257
+ */
+ failedNodePtr.p->m_remove_node_from_table_lcp_id = RNIL;
+ if (c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH.isWaitingFor(failedNodePtr.i))
+ {
+ jam();
+ failedNodePtr.p->m_remove_node_from_table_lcp_id = SYSFILE->latestLCP_ID;
+ }
+ jam();
+
+ if (!ERROR_INSERTED(7194))
+ {
+ signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
+ signal->theData[1] = failedNodePtr.i;
+ signal->theData[2] = 0; // Tab id
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ }
+ else
+ {
+ ndbout_c("7194 Not starting ZREMOVE_NODE_FROM_TABLE");
+ }
+
setLocalNodefailHandling(signal, failedNodePtr.i, NF_REMOVE_NODE_FROM_TABLE);
}//Dbdih::startRemoveFailedNode()
@@ -5710,6 +5849,11 @@ void Dbdih::removeNodeFromTable(Signal* signal,
return;
}//if
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ const Uint32 lcpId = nodePtr.p->m_remove_node_from_table_lcp_id;
+
/**
* For each fragment
*/
@@ -5717,7 +5861,6 @@ void Dbdih::removeNodeFromTable(Signal* signal,
Uint32 noOfRemovedLcpReplicas = 0; // No of replicas in LCP removed
Uint32 noOfRemainingLcpReplicas = 0;// No of replicas in LCP remaining
- //const Uint32 lcpId = SYSFILE->latestLCP_ID;
const bool lcpOngoingFlag = (tabPtr.p->tabLcpStatus== TabRecord::TLS_ACTIVE);
const bool unlogged = (tabPtr.p->tabStorage != TabRecord::ST_NORMAL);
@@ -5752,6 +5895,23 @@ void Dbdih::removeNodeFromTable(Signal* signal,
noOfRemovedLcpReplicas ++;
replicaPtr.p->lcpOngoingFlag = false;
}
+
+ if (lcpId != RNIL)
+ {
+ jam();
+ Uint32 lcpNo = prevLcpNo(replicaPtr.p->nextLcp);
+ if (replicaPtr.p->lcpStatus[lcpNo] == ZVALID &&
+ replicaPtr.p->lcpId[lcpNo] == SYSFILE->latestLCP_ID)
+ {
+ jam();
+ replicaPtr.p->lcpStatus[lcpNo] = ZINVALID;
+ replicaPtr.p->lcpId[lcpNo] = 0;
+ replicaPtr.p->nextLcp = lcpNo;
+ ndbout_c("REMOVING lcp: %u from table: %u frag: %u node: %u",
+ SYSFILE->latestLCP_ID,
+ tabPtr.i, fragNo, nodeId);
+ }
+ }
}
}
if (!found)
@@ -5982,12 +6142,22 @@ Dbdih::checkEmptyLcpComplete(Signal *signal){
signal->theData[0] = 7012;
execDUMP_STATE_ORD(signal);
+
+ if (ERROR_INSERTED(7194))
+ {
+ ndbout_c("7194 starting ZREMOVE_NODE_FROM_TABLE");
+ signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
+ signal->theData[1] = c_lcpMasterTakeOverState.failedNodeId;
+ signal->theData[2] = 0; // Tab id
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ }
c_lcpMasterTakeOverState.set(LMTOS_INITIAL, __LINE__);
MasterLCPReq * const req = (MasterLCPReq *)&signal->theData[0];
req->masterRef = reference();
req->failedNodeId = c_lcpMasterTakeOverState.failedNodeId;
sendLoopMacro(MASTER_LCPREQ, sendMASTER_LCPREQ);
+
} else {
sendMASTER_LCPCONF(signal);
}
@@ -6009,7 +6179,7 @@ void Dbdih::execMASTER_LCPREQ(Signal* signal)
jam();
ndbout_c("resending GSN_MASTER_LCPREQ");
sendSignalWithDelay(reference(), GSN_MASTER_LCPREQ, signal,
- signal->getLength(), 50);
+ 50, signal->getLength());
return;
}
Uint32 failedNodeId = req->failedNodeId;
@@ -6300,6 +6470,15 @@ void Dbdih::execMASTER_LCPCONF(Signal* signal)
{
const MasterLCPConf * const conf = (MasterLCPConf *)&signal->theData[0];
jamEntry();
+
+ if (ERROR_INSERTED(7194))
+ {
+ ndbout_c("delaying MASTER_LCPCONF due to error 7194");
+ sendSignalWithDelay(reference(), GSN_MASTER_LCPCONF, signal,
+ 300, signal->getLength());
+ return;
+ }
+
Uint32 senderNodeId = conf->senderNodeId;
MasterLCPConf::State lcpState = (MasterLCPConf::State)conf->lcpState;
const Uint32 failedNodeId = conf->failedNodeId;
@@ -6434,7 +6613,6 @@ void Dbdih::MASTER_LCPhandling(Signal* signal, Uint32 failedNodeId)
#endif
c_lcpState.keepGci = SYSFILE->keepGCI;
- c_lcpState.setLcpStatus(LCP_START_LCP_ROUND, __LINE__);
startLcpRoundLoopLab(signal, 0, 0);
break;
}
@@ -10412,6 +10590,8 @@ void Dbdih::sendLastLCP_FRAG_ORD(Signal* signal)
if(ERROR_INSERTED(7075)){
continue;
}
+
+ CRASH_INSERTION(7193);
BlockReference ref = calcLqhBlockRef(nodePtr.i);
sendSignal(ref, GSN_LCP_FRAG_ORD, signal,LcpFragOrd::SignalLength, JBB);
}
@@ -10524,6 +10704,12 @@ void Dbdih::execLCP_FRAG_REP(Signal* signal)
Uint32 started = lcpReport->maxGciStarted;
Uint32 completed = lcpReport->maxGciCompleted;
+ if (started > c_lcpState.lcpStopGcp)
+ {
+ jam();
+ c_lcpState.lcpStopGcp = started;
+ }
+
if(tableDone){
jam();
@@ -10639,6 +10825,13 @@ Dbdih::checkLcpAllTablesDoneInLqh(){
CRASH_INSERTION2(7017, !isMaster());
c_lcpState.setLcpStatus(LCP_TAB_COMPLETED, __LINE__);
+
+ if (ERROR_INSERTED(7194))
+ {
+ ndbout_c("CLEARING 7194");
+ CLEAR_ERROR_INSERT_VALUE;
+ }
+
return true;
}
@@ -10828,6 +11021,11 @@ Dbdih::sendLCP_FRAG_ORD(Signal* signal,
BlockReference ref = calcLqhBlockRef(replicaPtr.p->procNode);
+ if (ERROR_INSERTED(7193) && replicaPtr.p->procNode == getOwnNodeId())
+ {
+ return;
+ }
+
LcpFragOrd * const lcpFragOrd = (LcpFragOrd *)&signal->theData[0];
lcpFragOrd->tableId = info.tableId;
lcpFragOrd->fragmentId = info.fragId;
@@ -10904,6 +11102,8 @@ void Dbdih::execLCP_COMPLETE_REP(Signal* signal)
{
jamEntry();
+ CRASH_INSERTION(7191);
+
#if 0
g_eventLogger.info("LCP_COMPLETE_REP");
printLCP_COMPLETE_REP(stdout,
@@ -11050,7 +11250,12 @@ void Dbdih::allNodesLcpCompletedLab(Signal* signal)
signal->theData[0] = NDB_LE_LocalCheckpointCompleted; //Event type
signal->theData[1] = SYSFILE->latestLCP_ID;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
- c_lcpState.lcpStopGcp = c_newest_restorable_gci;
+
+ if (c_newest_restorable_gci > c_lcpState.lcpStopGcp)
+ {
+ jam();
+ c_lcpState.lcpStopGcp = c_newest_restorable_gci;
+ }
/**
* Start checking for next LCP
@@ -11920,13 +12125,12 @@ void Dbdih::findMinGci(ReplicaRecordPtr fmgReplicaPtr,
lcpNo = fmgReplicaPtr.p->nextLcp;
do {
ndbrequire(lcpNo < MAX_LCP_STORED);
- if (fmgReplicaPtr.p->lcpStatus[lcpNo] == ZVALID &&
- fmgReplicaPtr.p->maxGciStarted[lcpNo] < c_newest_restorable_gci)
+ if (fmgReplicaPtr.p->lcpStatus[lcpNo] == ZVALID)
{
jam();
keepGci = fmgReplicaPtr.p->maxGciCompleted[lcpNo];
oldestRestorableGci = fmgReplicaPtr.p->maxGciStarted[lcpNo];
- ndbrequire(((int)oldestRestorableGci) >= 0);
+ ndbassert(fmgReplicaPtr.p->maxGciStarted[lcpNo] <c_newest_restorable_gci);
return;
} else {
jam();
@@ -12828,6 +13032,7 @@ void Dbdih::newCrashedReplica(Uint32 nodeId, ReplicaRecordPtr ncrReplicaPtr)
void Dbdih::nodeResetStart()
{
jam();
+ c_nodeStartSlave.nodeId = 0;
c_nodeStartMaster.startNode = RNIL;
c_nodeStartMaster.failNr = cfailurenr;
c_nodeStartMaster.activeState = false;
@@ -13663,6 +13868,7 @@ void Dbdih::setLcpActiveStatusStart(Signal* signal)
// It must be taken over with the copy fragment process after a system
// crash. We indicate this by setting the active status to TAKE_OVER.
/*-------------------------------------------------------------------*/
+ c_lcpState.m_participatingLQH.set(nodePtr.i);
nodePtr.p->activeStatus = Sysfile::NS_TakeOver;
//break; // Fall through
case Sysfile::NS_TakeOver:{
@@ -13705,6 +13911,7 @@ void Dbdih::setLcpActiveStatusStart(Signal* signal)
break;
case Sysfile::NS_ActiveMissed_2:
jam();
+ CRASH_INSERTION(7192);
if ((nodePtr.p->nodeStatus == NodeRecord::ALIVE) &&
(!nodePtr.p->copyCompleted)) {
jam();
@@ -14370,6 +14577,14 @@ Dbdih::execDUMP_STATE_ORD(Signal* signal)
("immediateLcpStart = %d masterLcpNodeId = %d",
c_lcpState.immediateLcpStart,
refToNode(c_lcpState.m_masterLcpDihRef));
+
+ for (Uint32 i = 0; i<10; i++)
+ {
+ infoEvent("%u : status: %u place: %u", i,
+ c_lcpState.m_saveState[i].m_status,
+ c_lcpState.m_saveState[i].m_place);
+ }
+
infoEvent("-- Node %d LCP STATE --", getOwnNodeId());
}
diff --git a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
index 6f8e5569831..62add0cf503 100644
--- a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
+++ b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
@@ -585,7 +585,6 @@ public:
enum ExecSrStatus {
IDLE = 0,
- ACTIVE_REMOVE_AFTER = 1,
ACTIVE = 2
};
/**
@@ -869,11 +868,6 @@ public:
* heard of.
*/
Uint8 fragDistributionKey;
- /**
- * The identity of the next local checkpoint this fragment
- * should perform.
- */
- Uint8 nextLcp;
/**
* How many local checkpoints does the fragment contain
*/
@@ -2025,7 +2019,6 @@ public:
Uint8 reclenAiLqhkey;
Uint8 m_offset_current_keybuf;
Uint8 replicaType;
- Uint8 simpleRead;
Uint8 seqNoReplica;
Uint8 tcNodeFailrec;
Uint8 m_disk_table;
@@ -2098,10 +2091,6 @@ private:
void execEXEC_SRCONF(Signal* signal);
void execREAD_PSEUDO_REQ(Signal* signal);
- void build_acc(Signal*, Uint32 fragPtrI);
- void execBUILDINDXREF(Signal*signal);
- void execBUILDINDXCONF(Signal*signal);
-
void execDUMP_STATE_ORD(Signal* signal);
void execACC_ABORTCONF(Signal* signal);
void execNODE_FAILREP(Signal* signal);
@@ -2145,6 +2134,7 @@ private:
void execSTORED_PROCCONF(Signal* signal);
void execSTORED_PROCREF(Signal* signal);
void execCOPY_FRAGREQ(Signal* signal);
+ void execPREPARE_COPY_FRAG_REQ(Signal* signal);
void execUPDATE_FRAG_DIST_KEY_ORD(Signal*);
void execCOPY_ACTIVEREQ(Signal* signal);
void execCOPY_STATEREQ(Signal* signal);
@@ -2780,7 +2770,13 @@ private:
/*THIS VARIABLE KEEPS TRACK OF HOW MANY FRAGMENTS THAT PARTICIPATE IN */
/*EXECUTING THE LOG. IF ZERO WE DON'T NEED TO EXECUTE THE LOG AT ALL. */
/* ------------------------------------------------------------------------- */
- UintR cnoFragmentsExecSr;
+ Uint32 cnoFragmentsExecSr;
+
+ /**
+ * This is no of sent GSN_EXEC_FRAGREQ during this log phase
+ */
+ Uint32 cnoOutstandingExecFragReq;
+
/* ------------------------------------------------------------------------- */
/*THIS VARIABLE KEEPS TRACK OF WHICH OF THE FIRST TWO RESTART PHASES THAT */
/*HAVE COMPLETED. */
@@ -2801,7 +2797,6 @@ private:
DLFifoList<Fragrecord> c_lcp_waiting_fragments; // StartFragReq'ed
DLFifoList<Fragrecord> c_lcp_restoring_fragments; // Restoring as we speek
DLFifoList<Fragrecord> c_lcp_complete_fragments; // Restored
- DLFifoList<Fragrecord> c_redo_complete_fragments; // Redo'ed
/* ------------------------------------------------------------------------- */
/*USED DURING SYSTEM RESTART, INDICATES THE OLDEST GCI THAT CAN BE RESTARTED */
diff --git a/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp b/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp
index d6411ee1cb9..b3a3d512da7 100644
--- a/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp
@@ -168,7 +168,6 @@ Dblqh::Dblqh(Block_context& ctx):
c_lcp_waiting_fragments(c_fragment_pool),
c_lcp_restoring_fragments(c_fragment_pool),
c_lcp_complete_fragments(c_fragment_pool),
- c_redo_complete_fragments(c_fragment_pool),
m_commitAckMarkerHash(m_commitAckMarkerPool),
c_scanTakeOverHash(c_scanRecordPool)
{
@@ -295,9 +294,6 @@ Dblqh::Dblqh(Block_context& ctx):
addRecSignal(GSN_READ_PSEUDO_REQ, &Dblqh::execREAD_PSEUDO_REQ);
- addRecSignal(GSN_BUILDINDXREF, &Dblqh::execBUILDINDXREF);
- addRecSignal(GSN_BUILDINDXCONF, &Dblqh::execBUILDINDXCONF);
-
addRecSignal(GSN_DEFINE_BACKUP_REF, &Dblqh::execDEFINE_BACKUP_REF);
addRecSignal(GSN_DEFINE_BACKUP_CONF, &Dblqh::execDEFINE_BACKUP_CONF);
@@ -310,6 +306,9 @@ Dblqh::Dblqh(Block_context& ctx):
addRecSignal(GSN_UPDATE_FRAG_DIST_KEY_ORD,
&Dblqh::execUPDATE_FRAG_DIST_KEY_ORD);
+ addRecSignal(GSN_PREPARE_COPY_FRAG_REQ,
+ &Dblqh::execPREPARE_COPY_FRAG_REQ);
+
initData();
#ifdef VM_TRACE
diff --git a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
index efb88bfccd2..83d38595c1f 100644
--- a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
@@ -356,7 +356,6 @@ void Dblqh::execCONTINUEB(Signal* signal)
break;
case ZSR_PHASE3_START:
jam();
- signal->theData[0] = data0;
srPhase3Start(signal);
return;
break;
@@ -428,25 +427,25 @@ void Dblqh::execCONTINUEB(Signal* signal)
if (fragptr.i != RNIL)
{
jam();
- c_redo_complete_fragments.getPtr(fragptr);
+ c_lcp_complete_fragments.getPtr(fragptr);
signal->theData[0] = fragptr.p->tabRef;
signal->theData[1] = fragptr.p->fragId;
sendSignal(DBACC_REF, GSN_EXPANDCHECK2, signal, 2, JBB);
Ptr<Fragrecord> save = fragptr;
- c_redo_complete_fragments.next(fragptr);
+ c_lcp_complete_fragments.next(fragptr);
signal->theData[0] = ZENABLE_EXPAND_CHECK;
signal->theData[1] = fragptr.i;
sendSignal(DBLQH_REF, GSN_CONTINUEB, signal, 2, JBB);
- c_redo_complete_fragments.remove(save);
+ c_lcp_complete_fragments.remove(save);
return;
}
else
{
jam();
cstartRecReq = 2;
- ndbrequire(c_redo_complete_fragments.isEmpty());
+ ndbrequire(c_lcp_complete_fragments.isEmpty());
StartRecConf * conf = (StartRecConf*)signal->getDataPtrSend();
conf->startingNodeId = getOwnNodeId();
sendSignal(cmasterDihBlockref, GSN_START_RECCONF, signal,
@@ -495,8 +494,9 @@ void Dblqh::execINCL_NODEREQ(Signal* signal)
cnodeStatus[i] = ZNODE_UP;
}//if
}//for
- signal->theData[0] = cownref;
- sendSignal(retRef, GSN_INCL_NODECONF, signal, 1, JBB);
+ signal->theData[0] = nodeId;
+ signal->theData[1] = cownref;
+ sendSignal(retRef, GSN_INCL_NODECONF, signal, 2, JBB);
return;
}//Dblqh::execINCL_NODEREQ()
@@ -1121,7 +1121,6 @@ void Dblqh::execLQHFRAGREQ(Signal* signal)
Uint32 minRowsHigh = req->minRowsHigh;
Uint32 tschemaVersion = req->schemaVersion;
Uint32 ttupKeyLength = req->keyLength;
- Uint32 nextLcp = req->nextLCP;
Uint32 noOfKeyAttr = req->noOfKeyAttr;
Uint32 noOfCharsets = req->noOfCharsets;
Uint32 checksumIndicator = req->checksumIndicator;
@@ -1214,7 +1213,6 @@ void Dblqh::execLQHFRAGREQ(Signal* signal)
fragptr.p->lcpFlag = Fragrecord::LCP_STATE_FALSE;
}//if
- fragptr.p->nextLcp = nextLcp;
//----------------------------------------------
// For node restarts it is not necessarily zero
//----------------------------------------------
@@ -3496,7 +3494,6 @@ void Dblqh::execLQHKEYREQ(Signal* signal)
regTcPtr->dirtyOp = LqhKeyReq::getDirtyFlag(Treqinfo);
regTcPtr->opExec = LqhKeyReq::getInterpretedFlag(Treqinfo);
regTcPtr->opSimple = LqhKeyReq::getSimpleFlag(Treqinfo);
- regTcPtr->simpleRead = op == ZREAD && regTcPtr->opSimple;
regTcPtr->seqNoReplica = LqhKeyReq::getSeqNoReplica(Treqinfo);
UintR TreclenAiLqhkey = LqhKeyReq::getAIInLqhKeyReq(Treqinfo);
regTcPtr->apiVersionNo = 0;
@@ -3513,9 +3510,15 @@ void Dblqh::execLQHKEYREQ(Signal* signal)
regTcPtr->lockType =
op == ZREAD_EX ? ZUPDATE : (Operation_t) op == ZWRITE ? ZINSERT : (Operation_t) op;
}
+
+ if (regTcPtr->dirtyOp)
+ {
+ ndbrequire(regTcPtr->opSimple);
+ }
- CRASH_INSERTION2(5041, regTcPtr->simpleRead &&
- refToNode(signal->senderBlockRef()) != cownNodeid);
+ CRASH_INSERTION2(5041, (op == ZREAD &&
+ (regTcPtr->opSimple || regTcPtr->dirtyOp) &&
+ refToNode(signal->senderBlockRef()) != cownNodeid));
regTcPtr->reclenAiLqhkey = TreclenAiLqhkey;
regTcPtr->currReclenAi = TreclenAiLqhkey;
@@ -3665,6 +3668,7 @@ void Dblqh::execLQHKEYREQ(Signal* signal)
{
ndbout_c("fragptr.p->fragStatus: %d",
fragptr.p->fragStatus);
+ CRASH_INSERTION(5046);
}
ndbassert(fragptr.p->fragStatus == Fragrecord::ACTIVE_CREATION);
fragptr.p->m_copy_started_state = Fragrecord::AC_NR_COPY;
@@ -3687,8 +3691,8 @@ void Dblqh::execLQHKEYREQ(Signal* signal)
Uint8 TdistKey = LqhKeyReq::getDistributionKey(TtotReclenAi);
if ((tfragDistKey != TdistKey) &&
(regTcPtr->seqNoReplica == 0) &&
- (regTcPtr->dirtyOp == ZFALSE) &&
- (regTcPtr->simpleRead == ZFALSE)) {
+ (regTcPtr->dirtyOp == ZFALSE))
+ {
/* ----------------------------------------------------------------------
* WE HAVE DIFFERENT OPINION THAN THE DIH THAT STARTED THE TRANSACTION.
* THE REASON COULD BE THAT THIS IS AN OLD DISTRIBUTION WHICH IS NO LONGER
@@ -4778,7 +4782,18 @@ void Dblqh::tupkeyConfLab(Signal* signal)
TRACE_OP(regTcPtr, "TUPKEYCONF");
- if (regTcPtr->simpleRead) {
+ if (readLen != 0)
+ {
+ jam();
+
+ /* SET BIT 15 IN REQINFO */
+ LqhKeyReq::setApplicationAddressFlag(regTcPtr->reqinfo, 1);
+ regTcPtr->readlenAi = readLen;
+ }//if
+
+ if (regTcPtr->operation == ZREAD &&
+ (regTcPtr->opSimple || regTcPtr->dirtyOp))
+ {
jam();
/* ----------------------------------------------------------------------
* THE OPERATION IS A SIMPLE READ.
@@ -4792,14 +4807,6 @@ void Dblqh::tupkeyConfLab(Signal* signal)
commitContinueAfterBlockedLab(signal);
return;
}//if
- if (readLen != 0)
- {
- jam();
-
- /* SET BIT 15 IN REQINFO */
- LqhKeyReq::setApplicationAddressFlag(regTcPtr->reqinfo, 1);
- regTcPtr->readlenAi = readLen;
- }//if
regTcPtr->totSendlenAi = writeLen;
ndbrequire(regTcPtr->totSendlenAi == regTcPtr->currTupAiLen);
@@ -5178,12 +5185,15 @@ void Dblqh::packLqhkeyreqLab(Signal* signal)
/* */
/* ------------------------------------------------------------------------- */
sendLqhkeyconfTc(signal, regTcPtr->tcBlockref);
- if (regTcPtr->dirtyOp != ZTRUE) {
+ if (! (regTcPtr->dirtyOp ||
+ (regTcPtr->operation == ZREAD && regTcPtr->opSimple)))
+ {
jam();
regTcPtr->transactionState = TcConnectionrec::PREPARED;
releaseOprec(signal);
} else {
jam();
+
/*************************************************************>*/
/* DIRTY WRITES ARE USED IN TWO SITUATIONS. THE FIRST */
/* SITUATION IS WHEN THEY ARE USED TO UPDATE COUNTERS AND*/
@@ -6406,8 +6416,8 @@ void Dblqh::commitContinueAfterBlockedLab(Signal* signal)
Ptr<TcConnectionrec> regTcPtr = tcConnectptr;
Ptr<Fragrecord> regFragptr = fragptr;
Uint32 operation = regTcPtr.p->operation;
- Uint32 simpleRead = regTcPtr.p->simpleRead;
Uint32 dirtyOp = regTcPtr.p->dirtyOp;
+ Uint32 opSimple = regTcPtr.p->opSimple;
if (regTcPtr.p->activeCreat != Fragrecord::AC_IGNORED) {
if (operation != ZREAD) {
TupCommitReq * const tupCommitReq =
@@ -6465,20 +6475,29 @@ void Dblqh::commitContinueAfterBlockedLab(Signal* signal)
EXECUTE_DIRECT(acc, GSN_ACC_COMMITREQ, signal, 1);
}
- if (simpleRead) {
+ if (dirtyOp)
+ {
jam();
-/* ------------------------------------------------------------------------- */
-/*THE OPERATION WAS A SIMPLE READ THUS THE COMMIT PHASE IS ONLY NEEDED TO */
-/*RELEASE THE LOCKS. AT THIS POINT IN THE CODE THE LOCKS ARE RELEASED AND WE */
-/*ARE IN A POSITION TO SEND LQHKEYCONF TO TC. WE WILL ALSO RELEASE ALL */
-/*RESOURCES BELONGING TO THIS OPERATION SINCE NO MORE WORK WILL BE */
-/*PERFORMED. */
-/* ------------------------------------------------------------------------- */
+ /**
+ * The dirtyRead does not send anything but TRANSID_AI from LDM
+ */
fragptr = regFragptr;
tcConnectptr = regTcPtr;
cleanUp(signal);
return;
- }//if
+ }
+
+ /**
+ * The simpleRead will send a LQHKEYCONF
+ * but have already released the locks
+ */
+ if (opSimple)
+ {
+ fragptr = regFragptr;
+ tcConnectptr = regTcPtr;
+ packLqhkeyreqLab(signal);
+ return;
+ }
}
}//if
jamEntry();
@@ -7088,7 +7107,7 @@ void Dblqh::abortStateHandlerLab(Signal* signal)
/* ------------------------------------------------------------------------- */
return;
}//if
- if (regTcPtr->simpleRead) {
+ if (regTcPtr->opSimple) {
jam();
/* ------------------------------------------------------------------------- */
/*A SIMPLE READ IS CURRENTLY RELEASING THE LOCKS OR WAITING FOR ACCESS TO */
@@ -7356,7 +7375,8 @@ void Dblqh::continueAbortLab(Signal* signal)
void Dblqh::continueAfterLogAbortWriteLab(Signal* signal)
{
TcConnectionrec * const regTcPtr = tcConnectptr.p;
- if (regTcPtr->simpleRead) {
+ if (regTcPtr->operation == ZREAD && regTcPtr->dirtyOp)
+ {
jam();
TcKeyRef * const tcKeyRef = (TcKeyRef *) signal->getDataPtrSend();
@@ -8917,6 +8937,9 @@ void Dblqh::storedProcConfScanLab(Signal* signal)
case Fragrecord::REMOVING:
jam();
default:
+ jamLine(fragptr.p->fragStatus);
+ ndbout_c("fragptr.p->fragStatus: %u",
+ fragptr.p->fragStatus);
ndbrequire(false);
break;
}//switch
@@ -10062,6 +10085,86 @@ Dblqh::calculateHash(Uint32 tableId, const Uint32* src)
return md5_hash(Tmp, keyLen);
}//Dblqh::calculateHash()
+/**
+ * PREPARE COPY FRAG REQ
+ */
+void
+Dblqh::execPREPARE_COPY_FRAG_REQ(Signal* signal)
+{
+ jamEntry();
+ PrepareCopyFragReq req = *(PrepareCopyFragReq*)signal->getDataPtr();
+
+ CRASH_INSERTION(5045);
+
+ tabptr.i = req.tableId;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+
+ Uint32 max_page = RNIL;
+
+ if (getOwnNodeId() != req.startingNodeId)
+ {
+ jam();
+ /**
+ * This is currently dead code...
+ * but is provided so we can impl. a better scan+delete on
+ * starting node wo/ having to change running node
+ */
+ ndbrequire(getOwnNodeId() == req.copyNodeId);
+ c_tup->get_frag_info(req.tableId, req.fragId, &max_page);
+
+ PrepareCopyFragConf* conf = (PrepareCopyFragConf*)signal->getDataPtrSend();
+ conf->senderData = req.senderData;
+ conf->senderRef = reference();
+ conf->tableId = req.tableId;
+ conf->fragId = req.fragId;
+ conf->copyNodeId = req.copyNodeId;
+ conf->startingNodeId = req.startingNodeId;
+ conf->maxPageNo = max_page;
+ sendSignal(req.senderRef, GSN_PREPARE_COPY_FRAG_CONF,
+ signal, PrepareCopyFragConf::SignalLength, JBB);
+
+ return;
+ }
+
+ if (! DictTabInfo::isOrderedIndex(tabptr.p->tableType))
+ {
+ jam();
+ ndbrequire(getFragmentrec(signal, req.fragId));
+
+ /**
+ *
+ */
+ if (cstartType == NodeState::ST_SYSTEM_RESTART)
+ {
+ jam();
+ signal->theData[0] = fragptr.p->tabRef;
+ signal->theData[1] = fragptr.p->fragId;
+ sendSignal(DBACC_REF, GSN_EXPANDCHECK2, signal, 2, JBB);
+ }
+
+
+ /**
+ *
+ */
+ fragptr.p->m_copy_started_state = Fragrecord::AC_IGNORED;
+ fragptr.p->fragStatus = Fragrecord::ACTIVE_CREATION;
+ fragptr.p->logFlag = Fragrecord::STATE_FALSE;
+
+ c_tup->get_frag_info(req.tableId, req.fragId, &max_page);
+ }
+
+ PrepareCopyFragConf* conf = (PrepareCopyFragConf*)signal->getDataPtrSend();
+ conf->senderData = req.senderData;
+ conf->senderRef = reference();
+ conf->tableId = req.tableId;
+ conf->fragId = req.fragId;
+ conf->copyNodeId = req.copyNodeId;
+ conf->startingNodeId = req.startingNodeId;
+ conf->maxPageNo = max_page;
+ sendSignal(req.senderRef, GSN_PREPARE_COPY_FRAG_CONF,
+ signal, PrepareCopyFragConf::SignalLength, JBB);
+}
+
/* *************************************** */
/* COPY_FRAGREQ: Start copying a fragment */
/* *************************************** */
@@ -10097,6 +10200,13 @@ void Dblqh::execCOPY_FRAGREQ(Signal* signal)
for (i = 0; i<nodeCount; i++)
nodemask.set(copyFragReq->nodeList[i]);
}
+ Uint32 maxPage = copyFragReq->nodeList[nodeCount];
+ Uint32 version = getNodeInfo(refToNode(userRef)).m_version;
+ if (ndb_check_prep_copy_frag_version(version) < 2)
+ {
+ jam();
+ maxPage = RNIL;
+ }
if (DictTabInfo::isOrderedIndex(tabptr.p->tableType)) {
jam();
@@ -10172,14 +10282,15 @@ void Dblqh::execCOPY_FRAGREQ(Signal* signal)
req->requestInfo = 0;
AccScanReq::setLockMode(req->requestInfo, 0);
AccScanReq::setReadCommittedFlag(req->requestInfo, 0);
- AccScanReq::setNRScanFlag(req->requestInfo, gci ? 1 : 0);
+ AccScanReq::setNRScanFlag(req->requestInfo, 1);
AccScanReq::setNoDiskScanFlag(req->requestInfo, 1);
req->transId1 = tcConnectptr.p->transid[0];
req->transId2 = tcConnectptr.p->transid[1];
req->savePointId = tcConnectptr.p->savePointId;
+ req->maxPage = maxPage;
sendSignal(scanptr.p->scanBlockref, GSN_ACC_SCANREQ, signal,
- AccScanReq::SignalLength, JBB);
+ AccScanReq::SignalLength + 1, JBB);
if (! nodemask.isclear())
{
@@ -14031,15 +14142,12 @@ void Dblqh::execSTART_FRAGREQ(Signal* signal)
if (lcpNo == (MAX_LCP_STORED - 1)) {
jam();
fragptr.p->lcpId[lcpNo] = lcpId;
- fragptr.p->nextLcp = 0;
} else if (lcpNo < (MAX_LCP_STORED - 1)) {
jam();
fragptr.p->lcpId[lcpNo] = lcpId;
- fragptr.p->nextLcp = lcpNo + 1;
} else {
ndbrequire(lcpNo == ZNIL);
jam();
- fragptr.p->nextLcp = 0;
}//if
fragptr.p->srNoLognodes = noOfLogNodes;
fragptr.p->logFlag = Fragrecord::STATE_FALSE;
@@ -14071,24 +14179,19 @@ void Dblqh::execSTART_FRAGREQ(Signal* signal)
*/
c_lcp_complete_fragments.add(fragptr);
- if(lcpNo == ZNIL)
- {
- signal->theData[0] = tabptr.i;
- signal->theData[1] = fragId;
- sendSignal(DBACC_REF, GSN_EXPANDCHECK2, signal, 2, JBB);
- }
-
- if (getNodeState().getNodeRestartInProgress())
- {
- jam();
- fragptr.p->fragStatus = Fragrecord::ACTIVE_CREATION;
- }
-
- c_tup->disk_restart_mark_no_lcp(tabptr.i, fragId);
+ signal->theData[0] = tabptr.i;
+ signal->theData[1] = fragId;
+ sendSignal(DBACC_REF, GSN_EXPANDCHECK2, signal, 2, JBB);
+ c_tup->disk_restart_lcp_id(tabptr.i, fragId, RNIL);
jamEntry();
-
return;
- }//if
+ }
+ else
+ {
+ jam();
+ c_tup->disk_restart_lcp_id(tabptr.i, fragId, lcpId);
+ jamEntry();
+ }
c_lcpId = (c_lcpId == 0 ? lcpId : c_lcpId);
c_lcpId = (c_lcpId < lcpId ? c_lcpId : lcpId);
@@ -14280,65 +14383,9 @@ void Dblqh::execSTART_RECCONF(Signal* signal)
return;
}
- c_lcp_complete_fragments.first(fragptr);
- build_acc(signal, fragptr.i);
- return;
-}//Dblqh::execSTART_RECCONF()
-
-void
-Dblqh::build_acc(Signal* signal, Uint32 fragPtrI)
-{
- fragptr.i = fragPtrI;
- while(fragptr.i != RNIL)
- {
- c_lcp_complete_fragments.getPtr(fragptr);
- tabptr.i = fragptr.p->tabRef;
- ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
-
- if(true || fragptr.i != tabptr.p->fragrec[0])
- {
- // Only need to send 1 build per table, TUP will rebuild all
- fragptr.i = fragptr.p->nextList;
- continue;
- }
-
- BuildIndxReq* const req = (BuildIndxReq*)signal->getDataPtrSend();
- req->setUserRef(reference());
- req->setConnectionPtr(fragptr.i);
- req->setRequestType(BuildIndxReq::RT_SYSTEMRESTART);
- req->setBuildId(0); // not used
- req->setBuildKey(0); // not used
- req->setIndexType(RNIL);
- req->setIndexId(RNIL);
- req->setTableId(tabptr.i);
- req->setParallelism(0);
-
- sendSignal(DBTUP_REF, GSN_BUILDINDXREQ, signal,
- BuildIndxReq::SignalLength, JBB);
- return;
- }
-
startExecSr(signal);
}
-void
-Dblqh::execBUILDINDXREF(Signal* signal)
-{
- ndbrequire(false);
-}
-
-void
-Dblqh::execBUILDINDXCONF(Signal* signal)
-{
- BuildIndxConf* conf = (BuildIndxConf*)signal->getDataPtrSend();
- Uint32 fragPtrI = conf->getConnectionPtr();
-
- fragptr.i = fragPtrI;
- c_fragment_pool.getPtr(fragptr);
- infoEvent("LQH: primary key index %u rebuild done", fragptr.p->tabRef);
- build_acc(signal, fragptr.p->nextList);
-}
-
/* ***************> */
/* START_RECREF > */
/* ***************> */
@@ -14357,9 +14404,9 @@ void Dblqh::execSTART_EXEC_SR(Signal* signal)
fragptr.i = signal->theData[0];
Uint32 next = RNIL;
- if (fragptr.i == RNIL) {
+ if (fragptr.i == RNIL)
+ {
jam();
- ndbrequire(cnoOfNodes < MAX_NDB_NODES);
/* ----------------------------------------------------------------------
* NO MORE FRAGMENTS TO START EXECUTING THE LOG ON.
* SEND EXEC_SRREQ TO ALL LQH TO INDICATE THAT THIS NODE WILL
@@ -14375,10 +14422,15 @@ void Dblqh::execSTART_EXEC_SR(Signal* signal)
} else {
jam();
c_lcp_complete_fragments.getPtr(fragptr);
- if (fragptr.p->srNoLognodes > csrPhasesCompleted) {
+ next = fragptr.p->nextList;
+
+ if (fragptr.p->srNoLognodes > csrPhasesCompleted)
+ {
jam();
+ cnoOutstandingExecFragReq++;
+
Uint32 index = csrPhasesCompleted;
- arrGuard(index, 4);
+ arrGuard(index, MAX_LOG_EXEC);
BlockReference ref = calcLqhBlockRef(fragptr.p->srLqhLognode[index]);
fragptr.p->srStatus = Fragrecord::SS_STARTED;
@@ -14397,34 +14449,7 @@ void Dblqh::execSTART_EXEC_SR(Signal* signal)
sendSignal(ref, GSN_EXEC_FRAGREQ, signal,
ExecFragReq::SignalLength, JBB);
- next = fragptr.p->nextList;
- } else {
- jam();
- /* --------------------------------------------------------------------
- * THIS FRAGMENT IS NOW FINISHED WITH THE SYSTEM RESTART. IT DOES
- * NOT NEED TO PARTICIPATE IN ANY MORE PHASES. REMOVE IT FROM THE
- * LIST OF COMPLETED FRAGMENTS TO EXECUTE THE LOG ON.
- * ALSO SEND START_FRAGCONF TO DIH AND SET THE STATE TO ACTIVE ON THE
- * FRAGMENT.
- * ------------------------------------------------------------------- */
- next = fragptr.p->nextList;
- c_lcp_complete_fragments.remove(fragptr);
- c_redo_complete_fragments.add(fragptr);
-
- if (!getNodeState().getNodeRestartInProgress())
- {
- fragptr.p->logFlag = Fragrecord::STATE_TRUE;
- fragptr.p->fragStatus = Fragrecord::FSACTIVE;
- }
- else
- {
- fragptr.p->fragStatus = Fragrecord::ACTIVE_CREATION;
- }
- signal->theData[0] = fragptr.p->srUserptr;
- signal->theData[1] = cownNodeid;
- sendSignal(fragptr.p->srBlockref, GSN_START_FRAGCONF, signal, 2, JBB);
-
- } //if
+ }
signal->theData[0] = next;
sendSignal(cownref, GSN_START_EXEC_SR, signal, 1, JBB);
}//if
@@ -14445,24 +14470,8 @@ void Dblqh::execEXEC_FRAGREQ(Signal* signal)
tabptr.i = execFragReq->tableId;
Uint32 fragId = execFragReq->fragId;
ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
- if (!getFragmentrec(signal, fragId)) {
- jam();
- if (!insertFragrec(signal, fragId)) {
- jam();
- sendExecFragRefLab(signal);
- return;
- }//if
- initFragrec(signal, tabptr.i, fragId, ZLOG_NODE);
- fragptr.p->execSrStatus = Fragrecord::ACTIVE_REMOVE_AFTER;
- } else {
- jam();
- if (fragptr.p->execSrStatus == Fragrecord::ACTIVE_REMOVE_AFTER) {
- jam();
- fragptr.p->execSrStatus = Fragrecord::ACTIVE_REMOVE_AFTER;
- } else {
- jam();
- }//if
- }//if
+ ndbrequire(getFragmentrec(signal, fragId));
+
ndbrequire(fragptr.p->execSrNoReplicas < 4);
fragptr.p->execSrBlockref[fragptr.p->execSrNoReplicas] = execFragReq->userRef;
fragptr.p->execSrUserptr[fragptr.p->execSrNoReplicas] = execFragReq->userPtr;
@@ -14495,6 +14504,21 @@ void Dblqh::execEXEC_FRAGCONF(Signal* signal)
fragptr.i = signal->theData[0];
c_fragment_pool.getPtr(fragptr);
fragptr.p->srStatus = Fragrecord::SS_COMPLETED;
+
+ ndbrequire(cnoOutstandingExecFragReq);
+ cnoOutstandingExecFragReq--;
+ if (fragptr.p->srNoLognodes == csrPhasesCompleted + 1)
+ {
+ jam();
+
+ fragptr.p->logFlag = Fragrecord::STATE_TRUE;
+ fragptr.p->fragStatus = Fragrecord::FSACTIVE;
+
+ signal->theData[0] = fragptr.p->srUserptr;
+ signal->theData[1] = cownNodeid;
+ sendSignal(fragptr.p->srBlockref, GSN_START_FRAGCONF, signal, 2, JBB);
+ }
+
return;
}//Dblqh::execEXEC_FRAGCONF()
@@ -14518,6 +14542,7 @@ void Dblqh::execEXEC_SRCONF(Signal* signal)
Uint32 nodeId = signal->theData[0];
arrGuard(nodeId, MAX_NDB_NODES);
m_sr_exec_sr_conf.set(nodeId);
+
if (!m_sr_nodes.equal(m_sr_exec_sr_conf))
{
jam();
@@ -14538,16 +14563,8 @@ void Dblqh::execEXEC_SRCONF(Signal* signal)
* NOW CHECK IF ALL FRAGMENTS IN THIS PHASE HAVE COMPLETED. IF SO START THE
* NEXT PHASE.
* ----------------------------------------------------------------------- */
- c_lcp_complete_fragments.first(fragptr);
- while (fragptr.i != RNIL)
- {
- jam();
- if(fragptr.p->srStatus != Fragrecord::SS_COMPLETED)
- {
- return;
- }
- c_lcp_complete_fragments.next(fragptr);
- }
+ ndbrequire(cnoOutstandingExecFragReq == 0);
+
execSrCompletedLab(signal);
return;
}//Dblqh::execEXEC_SRCONF()
@@ -14603,6 +14620,7 @@ void Dblqh::execSrCompletedLab(Signal* signal)
* THERE ARE YET MORE PHASES TO RESTART.
* WE MUST INITIALISE DATA FOR NEXT PHASE AND SEND START SIGNAL.
* --------------------------------------------------------------------- */
+ csrPhaseStarted = ZSR_PHASE1_COMPLETED; // Set correct state first...
startExecSr(signal);
}//if
return;
@@ -14676,7 +14694,8 @@ void Dblqh::srPhase3Start(Signal* signal)
UintR tsrPhaseStarted;
jamEntry();
- tsrPhaseStarted = signal->theData[0];
+
+ tsrPhaseStarted = signal->theData[1];
if (csrPhaseStarted == ZSR_NO_PHASE_STARTED) {
jam();
csrPhaseStarted = tsrPhaseStarted;
@@ -15853,18 +15872,6 @@ void Dblqh::sendExecConf(Signal* signal)
sendSignal(fragptr.p->execSrBlockref[i], GSN_EXEC_FRAGCONF,
signal, 1, JBB);
}//for
- if (fragptr.p->execSrStatus == Fragrecord::ACTIVE) {
- jam();
- fragptr.p->execSrStatus = Fragrecord::IDLE;
- } else {
- ndbrequire(fragptr.p->execSrStatus == Fragrecord::ACTIVE_REMOVE_AFTER);
- jam();
- Uint32 fragId = fragptr.p->fragId;
- tabptr.i = fragptr.p->tabRef;
- ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
- c_lcp_complete_fragments.remove(fragptr);
- deleteFragrec(fragId);
- }//if
fragptr.p->execSrNoReplicas = 0;
}//if
loopCount++;
@@ -15892,17 +15899,10 @@ void Dblqh::sendExecConf(Signal* signal)
void Dblqh::srPhase3Comp(Signal* signal)
{
jamEntry();
- ndbrequire(cnoOfNodes < MAX_NDB_NODES);
- for (Uint32 i = 0; i < cnoOfNodes; i++) {
- jam();
- if (cnodeStatus[i] == ZNODE_UP) {
- jam();
- ndbrequire(cnodeData[i] < MAX_NDB_NODES);
- BlockReference ref = calcLqhBlockRef(cnodeData[i]);
- signal->theData[0] = cownNodeid;
- sendSignal(ref, GSN_EXEC_SRCONF, signal, 1, JBB);
- }//if
- }//for
+
+ signal->theData[0] = cownNodeid;
+ NodeReceiverGroup rg(DBLQH, m_sr_nodes);
+ sendSignal(rg, GSN_EXEC_SRCONF, signal, 1, JBB);
return;
}//Dblqh::srPhase3Comp()
@@ -16144,7 +16144,7 @@ void Dblqh::srFourthComp(Signal* signal)
if(cstartType == NodeState::ST_SYSTEM_RESTART)
{
jam();
- if (c_redo_complete_fragments.first(fragptr))
+ if (c_lcp_complete_fragments.first(fragptr))
{
jam();
signal->theData[0] = ZENABLE_EXPAND_CHECK;
@@ -17252,7 +17252,6 @@ void Dblqh::initFragrec(Signal* signal,
fragptr.p->maxGciInLcp = 0;
fragptr.p->copyFragState = ZIDLE;
fragptr.p->newestGci = cnewestGci;
- fragptr.p->nextLcp = 0;
fragptr.p->tabRef = tableId;
fragptr.p->fragId = fragId;
fragptr.p->srStatus = Fragrecord::SS_IDLE;
@@ -18341,6 +18340,7 @@ void Dblqh::sendLqhTransconf(Signal* signal, LqhTransConf::OperationStatus stat)
void Dblqh::startExecSr(Signal* signal)
{
cnoFragmentsExecSr = 0;
+ cnoOutstandingExecFragReq = 0;
c_lcp_complete_fragments.first(fragptr);
signal->theData[0] = fragptr.i;
sendSignal(cownref, GSN_START_EXEC_SR, signal, 1, JBB);
@@ -19022,7 +19022,6 @@ Dblqh::execDUMP_STATE_ORD(Signal* signal)
ndbout << " operation = " << tcRec.p->operation<<endl;
ndbout << " tcNodeFailrec = " << tcRec.p->tcNodeFailrec
<< " seqNoReplica = " << tcRec.p->seqNoReplica
- << " simpleRead = " << tcRec.p->simpleRead
<< endl;
ndbout << " replicaType = " << tcRec.p->replicaType
<< " reclenAiLqhkey = " << tcRec.p->reclenAiLqhkey
diff --git a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
index 75d79ba737f..db9f1454548 100644
--- a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
+++ b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
@@ -734,6 +734,7 @@ public:
// Index op return context
UintR indexOp;
UintR clientData;
+ Uint32 errorData;
UintR attrInfoLen;
UintR accumulatingIndexOp;
@@ -786,6 +787,7 @@ public:
UintR apiConnect; /* POINTER TO API CONNECT RECORD */
UintR nextTcConnect; /* NEXT TC RECORD*/
Uint8 dirtyOp;
+ Uint8 opSimple;
Uint8 lastReplicaNo; /* NUMBER OF THE LAST REPLICA IN THE OPERATION */
Uint8 noOfNodes; /* TOTAL NUMBER OF NODES IN OPERATION */
Uint8 operation; /* OPERATION TYPE */
@@ -886,13 +888,8 @@ public:
Uint8 opExec;
Uint8 unused;
+ Uint8 unused1;
- /**
- * IS THE OPERATION A SIMPLE TRANSACTION
- * 0 = NO, 1 = YES
- */
- Uint8 opSimple;
-
//---------------------------------------------------
// Second 16 byte cache line in second 64 byte cache
// line. Diverse use.
@@ -1464,7 +1461,7 @@ private:
void releaseAttrinfo();
void releaseGcp(Signal* signal);
void releaseKeys();
- void releaseSimpleRead(Signal*, ApiConnectRecordPtr, TcConnectRecord*);
+ void releaseDirtyRead(Signal*, ApiConnectRecordPtr, TcConnectRecord*);
void releaseDirtyWrite(Signal* signal);
void releaseTcCon();
void releaseTcConnectFail(Signal* signal);
@@ -1620,7 +1617,7 @@ private:
void startphase1x010Lab(Signal* signal);
void lqhKeyConf_checkTransactionState(Signal * signal,
- ApiConnectRecord * const regApiPtr);
+ Ptr<ApiConnectRecord> regApiPtr);
void checkDropTab(Signal* signal);
@@ -1675,6 +1672,7 @@ private:
UintR cfailure_nr;
UintR coperationsize;
UintR ctcTimer;
+ UintR cDbHbInterval;
ApiConnectRecordPtr tmpApiConnectptr;
UintR tcheckGcpId;
diff --git a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
index 887e6f848b1..e584883e3b6 100644
--- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
@@ -20,6 +20,7 @@
#include <RefConvert.hpp>
#include <ndb_limits.h>
#include <my_sys.h>
+#include <ndb_rand.h>
#include <signaldata/EventReport.hpp>
#include <signaldata/TcKeyReq.hpp>
@@ -309,9 +310,11 @@ void Dbtc::execINCL_NODEREQ(Signal* signal)
hostptr.i = signal->theData[1];
ptrCheckGuard(hostptr, chostFilesize, hostRecord);
hostptr.p->hostStatus = HS_ALIVE;
- signal->theData[0] = cownref;
c_alive_nodes.set(hostptr.i);
+ signal->theData[0] = hostptr.i;
+ signal->theData[1] = cownref;
+
if (ERROR_INSERTED(8039))
{
CLEAR_ERROR_INSERT_VALUE;
@@ -320,11 +323,11 @@ void Dbtc::execINCL_NODEREQ(Signal* signal)
sendSignal(numberToRef(CMVMI, hostptr.i),
GSN_NDB_TAMPER, signal, 1, JBB);
signal->theData[0] = save;
- sendSignalWithDelay(tblockref, GSN_INCL_NODECONF, signal, 5000, 1);
+ sendSignalWithDelay(tblockref, GSN_INCL_NODECONF, signal, 5000, 2);
return;
}
- sendSignal(tblockref, GSN_INCL_NODECONF, signal, 1, JBB);
+ sendSignal(tblockref, GSN_INCL_NODECONF, signal, 2, JBB);
}
void Dbtc::execREAD_NODESREF(Signal* signal)
@@ -658,6 +661,10 @@ void Dbtc::execREAD_CONFIG_REQ(Signal* signal)
ndb_mgm_get_int_parameter(p, CFG_DB_TRANSACTION_DEADLOCK_TIMEOUT, &val);
set_timeout_value(val);
+ val = 1500;
+ ndb_mgm_get_int_parameter(p, CFG_DB_HEARTBEAT_INTERVAL, &val);
+ cDbHbInterval = (val < 10) ? 10 : val;
+
val = 3000;
ndb_mgm_get_int_parameter(p, CFG_DB_TRANSACTION_INACTIVE_TIMEOUT, &val);
set_appl_timeout_value(val);
@@ -2790,9 +2797,9 @@ void Dbtc::execTCKEYREQ(Signal* signal)
Uint8 TNoDiskFlag = TcKeyReq::getNoDiskFlag(Treqinfo);
Uint8 TexecuteFlag = TexecFlag;
- regCachePtr->opSimple = TSimpleFlag;
- regCachePtr->opExec = TInterpretedFlag;
regTcPtr->dirtyOp = TDirtyFlag;
+ regTcPtr->opSimple = TSimpleFlag;
+ regCachePtr->opExec = TInterpretedFlag;
regCachePtr->distributionKeyIndicator = TDistrKeyFlag;
regCachePtr->m_no_disk_flag = TNoDiskFlag;
@@ -3246,9 +3253,10 @@ void Dbtc::sendlqhkeyreq(Signal* signal,
LqhKeyReq::setScanTakeOverFlag(tslrAttrLen, regCachePtr->scanTakeOverInd);
Tdata10 = 0;
- sig0 = regCachePtr->opSimple;
+ sig0 = regTcPtr->opSimple;
sig1 = regTcPtr->operation;
- bool simpleRead = (sig1 == ZREAD && sig0 == ZTRUE);
+ sig2 = regTcPtr->dirtyOp;
+ bool dirtyRead = (sig1 == ZREAD && sig2 == ZTRUE);
LqhKeyReq::setKeyLen(Tdata10, regCachePtr->keylen);
LqhKeyReq::setLastReplicaNo(Tdata10, regTcPtr->lastReplicaNo);
if (unlikely(version < NDBD_ROWID_VERSION))
@@ -3261,7 +3269,7 @@ void Dbtc::sendlqhkeyreq(Signal* signal,
// Indicate Application Reference is present in bit 15
/* ---------------------------------------------------------------------- */
LqhKeyReq::setApplicationAddressFlag(Tdata10, 1);
- LqhKeyReq::setDirtyFlag(Tdata10, regTcPtr->dirtyOp);
+ LqhKeyReq::setDirtyFlag(Tdata10, sig2);
LqhKeyReq::setInterpretedFlag(Tdata10, regCachePtr->opExec);
LqhKeyReq::setSimpleFlag(Tdata10, sig0);
LqhKeyReq::setOperation(Tdata10, sig1);
@@ -3322,7 +3330,7 @@ void Dbtc::sendlqhkeyreq(Signal* signal,
sig5 = regTcPtr->clientData;
sig6 = regCachePtr->scanInfo;
- if (! simpleRead)
+ if (! dirtyRead)
{
regApiPtr->m_transaction_nodes.set(regTcPtr->tcNodedata[0]);
regApiPtr->m_transaction_nodes.set(regTcPtr->tcNodedata[1]);
@@ -3395,7 +3403,6 @@ void Dbtc::packLqhkeyreq040Lab(Signal* signal,
BlockReference TBRef)
{
TcConnectRecord * const regTcPtr = tcConnectptr.p;
- CacheRecord * const regCachePtr = cachePtr.p;
#ifdef ERROR_INSERT
ApiConnectRecord * const regApiPtr = apiConnectptr.p;
if (ERROR_INSERTED(8009)) {
@@ -3420,8 +3427,8 @@ void Dbtc::packLqhkeyreq040Lab(Signal* signal,
if (anAttrBufIndex == RNIL) {
UintR TtcTimer = ctcTimer;
UintR Tread = (regTcPtr->operation == ZREAD);
- UintR Tsimple = (regCachePtr->opSimple == ZTRUE);
- UintR Tboth = Tread & Tsimple;
+ UintR Tdirty = (regTcPtr->dirtyOp == ZTRUE);
+ UintR Tboth = Tread & Tdirty;
setApiConTimer(apiConnectptr.i, TtcTimer, __LINE__);
jam();
/*--------------------------------------------------------------------
@@ -3430,7 +3437,7 @@ void Dbtc::packLqhkeyreq040Lab(Signal* signal,
releaseAttrinfo();
if (Tboth) {
jam();
- releaseSimpleRead(signal, apiConnectptr, tcConnectptr.p);
+ releaseDirtyRead(signal, apiConnectptr, tcConnectptr.p);
return;
}//if
regTcPtr->tcConnectstate = OS_OPERATING;
@@ -3490,11 +3497,11 @@ void Dbtc::releaseAttrinfo()
}//Dbtc::releaseAttrinfo()
/* ========================================================================= */
-/* ------- RELEASE ALL RECORDS CONNECTED TO A SIMPLE OPERATION ------- */
+/* ------- RELEASE ALL RECORDS CONNECTED TO A DIRTY OPERATION ------- */
/* ========================================================================= */
-void Dbtc::releaseSimpleRead(Signal* signal,
- ApiConnectRecordPtr regApiPtr,
- TcConnectRecord* regTcPtr)
+void Dbtc::releaseDirtyRead(Signal* signal,
+ ApiConnectRecordPtr regApiPtr,
+ TcConnectRecord* regTcPtr)
{
Uint32 Ttckeyrec = regApiPtr.p->tckeyrec;
Uint32 TclientData = regTcPtr->clientData;
@@ -3504,7 +3511,7 @@ void Dbtc::releaseSimpleRead(Signal* signal,
ConnectionState state = regApiPtr.p->apiConnectstate;
regApiPtr.p->tcSendArray[Ttckeyrec] = TclientData;
- regApiPtr.p->tcSendArray[Ttckeyrec + 1] = TcKeyConf::SimpleReadBit | Tnode;
+ regApiPtr.p->tcSendArray[Ttckeyrec + 1] = TcKeyConf::DirtyReadBit | Tnode;
regApiPtr.p->tckeyrec = Ttckeyrec + 2;
unlinkReadyTcCon(signal);
@@ -3534,8 +3541,8 @@ void Dbtc::releaseSimpleRead(Signal* signal,
/**
* Emulate LQHKEYCONF
*/
- lqhKeyConf_checkTransactionState(signal, regApiPtr.p);
-}//Dbtc::releaseSimpleRead()
+ lqhKeyConf_checkTransactionState(signal, regApiPtr);
+}//Dbtc::releaseDirtyRead()
/* ------------------------------------------------------------------------- */
/* ------- CHECK IF ALL TC CONNECTIONS ARE COMPLETED ------- */
@@ -3717,12 +3724,13 @@ void Dbtc::execLQHKEYCONF(Signal* signal)
TCKEY_abort(signal, 29);
return;
}//if
- ApiConnectRecord * const regApiPtr =
- &localApiConnectRecord[TapiConnectptrIndex];
+ Ptr<ApiConnectRecord> regApiPtr;
+ regApiPtr.i = TapiConnectptrIndex;
+ regApiPtr.p = &localApiConnectRecord[TapiConnectptrIndex];
apiConnectptr.i = TapiConnectptrIndex;
- apiConnectptr.p = regApiPtr;
- compare_transid1 = regApiPtr->transid[0] ^ Ttrans1;
- compare_transid2 = regApiPtr->transid[1] ^ Ttrans2;
+ apiConnectptr.p = regApiPtr.p;
+ compare_transid1 = regApiPtr.p->transid[0] ^ Ttrans1;
+ compare_transid2 = regApiPtr.p->transid[1] ^ Ttrans2;
compare_transid1 = compare_transid1 | compare_transid2;
if (compare_transid1 != 0) {
warningReport(signal, 24);
@@ -3734,25 +3742,25 @@ void Dbtc::execLQHKEYCONF(Signal* signal)
systemErrorLab(signal, __LINE__);
}//if
if (ERROR_INSERTED(8003)) {
- if (regApiPtr->apiConnectstate == CS_STARTED) {
+ if (regApiPtr.p->apiConnectstate == CS_STARTED) {
CLEAR_ERROR_INSERT_VALUE;
return;
}//if
}//if
if (ERROR_INSERTED(8004)) {
- if (regApiPtr->apiConnectstate == CS_RECEIVING) {
+ if (regApiPtr.p->apiConnectstate == CS_RECEIVING) {
CLEAR_ERROR_INSERT_VALUE;
return;
}//if
}//if
if (ERROR_INSERTED(8005)) {
- if (regApiPtr->apiConnectstate == CS_REC_COMMITTING) {
+ if (regApiPtr.p->apiConnectstate == CS_REC_COMMITTING) {
CLEAR_ERROR_INSERT_VALUE;
return;
}//if
}//if
if (ERROR_INSERTED(8006)) {
- if (regApiPtr->apiConnectstate == CS_START_COMMITTING) {
+ if (regApiPtr.p->apiConnectstate == CS_START_COMMITTING) {
CLEAR_ERROR_INSERT_VALUE;
return;
}//if
@@ -3767,10 +3775,12 @@ void Dbtc::execLQHKEYCONF(Signal* signal)
regTcPtr->lastLqhNodeId = refToNode(tlastLqhBlockref);
regTcPtr->noFiredTriggers = noFired;
- UintR Ttckeyrec = (UintR)regApiPtr->tckeyrec;
+ UintR Ttckeyrec = (UintR)regApiPtr.p->tckeyrec;
UintR TclientData = regTcPtr->clientData;
UintR TdirtyOp = regTcPtr->dirtyOp;
- ConnectionState TapiConnectstate = regApiPtr->apiConnectstate;
+ Uint32 TopSimple = regTcPtr->opSimple;
+ Uint32 Toperation = regTcPtr->operation;
+ ConnectionState TapiConnectstate = regApiPtr.p->apiConnectstate;
if (Ttckeyrec > (ZTCOPCONF_SIZE - 2)) {
TCKEY_abort(signal, 30);
return;
@@ -3795,23 +3805,34 @@ void Dbtc::execLQHKEYCONF(Signal* signal)
* since they will enter execLQHKEYCONF a second time
* Skip counting internally generated TcKeyReq
*/
- regApiPtr->tcSendArray[Ttckeyrec] = TclientData;
- regApiPtr->tcSendArray[Ttckeyrec + 1] = treadlenAi;
- regApiPtr->tckeyrec = Ttckeyrec + 2;
+ regApiPtr.p->tcSendArray[Ttckeyrec] = TclientData;
+ regApiPtr.p->tcSendArray[Ttckeyrec + 1] = treadlenAi;
+ regApiPtr.p->tckeyrec = Ttckeyrec + 2;
}//if
}//if
- if (TdirtyOp == ZTRUE) {
- UintR Tlqhkeyreqrec = regApiPtr->lqhkeyreqrec;
+ if (TdirtyOp == ZTRUE)
+ {
+ UintR Tlqhkeyreqrec = regApiPtr.p->lqhkeyreqrec;
jam();
releaseDirtyWrite(signal);
- regApiPtr->lqhkeyreqrec = Tlqhkeyreqrec - 1;
- } else {
+ regApiPtr.p->lqhkeyreqrec = Tlqhkeyreqrec - 1;
+ }
+ else if (Toperation == ZREAD && TopSimple)
+ {
+ UintR Tlqhkeyreqrec = regApiPtr.p->lqhkeyreqrec;
+ jam();
+ unlinkReadyTcCon(signal);
+ releaseTcCon();
+ regApiPtr.p->lqhkeyreqrec = Tlqhkeyreqrec - 1;
+ }
+ else
+ {
jam();
if (noFired == 0) {
jam();
// No triggers to execute
- UintR Tlqhkeyconfrec = regApiPtr->lqhkeyconfrec;
- regApiPtr->lqhkeyconfrec = Tlqhkeyconfrec + 1;
+ UintR Tlqhkeyconfrec = regApiPtr.p->lqhkeyconfrec;
+ regApiPtr.p->lqhkeyconfrec = Tlqhkeyconfrec + 1;
regTcPtr->tcConnectstate = OS_PREPARED;
}
}//if
@@ -3841,21 +3862,18 @@ void Dbtc::execLQHKEYCONF(Signal* signal)
jam();
if (regTcPtr->isIndexOp) {
jam();
- setupIndexOpReturn(regApiPtr, regTcPtr);
+ setupIndexOpReturn(regApiPtr.p, regTcPtr);
}
lqhKeyConf_checkTransactionState(signal, regApiPtr);
} else {
// We have fired triggers
jam();
saveTriggeringOpState(signal, regTcPtr);
- if (regTcPtr->noReceivedTriggers == noFired) {
- ApiConnectRecordPtr transPtr;
-
+ if (regTcPtr->noReceivedTriggers == noFired)
+ {
// We have received all data
jam();
- transPtr.i = TapiConnectptrIndex;
- transPtr.p = regApiPtr;
- executeTriggers(signal, &transPtr);
+ executeTriggers(signal, &regApiPtr);
}
// else wait for more trigger data
}
@@ -3879,7 +3897,7 @@ void Dbtc::setupIndexOpReturn(ApiConnectRecord* regApiPtr,
*/
void
Dbtc::lqhKeyConf_checkTransactionState(Signal * signal,
- ApiConnectRecord * const apiConnectPtrP)
+ Ptr<ApiConnectRecord> regApiPtr)
{
/*---------------------------------------------------------------*/
/* IF THE COMMIT FLAG IS SET IN SIGNAL TCKEYREQ THEN DBTC HAS TO */
@@ -3890,9 +3908,9 @@ Dbtc::lqhKeyConf_checkTransactionState(Signal * signal,
/* FOR ALL OPERATIONS, AND THEN WAIT FOR THE API TO CONCLUDE THE */
/* TRANSACTION */
/*---------------------------------------------------------------*/
- ConnectionState TapiConnectstate = apiConnectPtrP->apiConnectstate;
- UintR Tlqhkeyconfrec = apiConnectPtrP->lqhkeyconfrec;
- UintR Tlqhkeyreqrec = apiConnectPtrP->lqhkeyreqrec;
+ ConnectionState TapiConnectstate = regApiPtr.p->apiConnectstate;
+ UintR Tlqhkeyconfrec = regApiPtr.p->lqhkeyconfrec;
+ UintR Tlqhkeyreqrec = regApiPtr.p->lqhkeyreqrec;
int TnoOfOutStanding = Tlqhkeyreqrec - Tlqhkeyconfrec;
switch (TapiConnectstate) {
@@ -3902,11 +3920,11 @@ Dbtc::lqhKeyConf_checkTransactionState(Signal * signal,
diverify010Lab(signal);
return;
} else if (TnoOfOutStanding > 0) {
- if (apiConnectPtrP->tckeyrec == ZTCOPCONF_SIZE) {
+ if (regApiPtr.p->tckeyrec == ZTCOPCONF_SIZE) {
jam();
sendtckeyconf(signal, 0);
return;
- } else if (apiConnectPtrP->indexOpReturn) {
+ } else if (regApiPtr.p->indexOpReturn) {
jam();
sendtckeyconf(signal, 0);
return;
@@ -3925,11 +3943,11 @@ Dbtc::lqhKeyConf_checkTransactionState(Signal * signal,
sendtckeyconf(signal, 2);
return;
} else {
- if (apiConnectPtrP->tckeyrec == ZTCOPCONF_SIZE) {
+ if (regApiPtr.p->tckeyrec == ZTCOPCONF_SIZE) {
jam();
sendtckeyconf(signal, 0);
return;
- } else if (apiConnectPtrP->indexOpReturn) {
+ } else if (regApiPtr.p->indexOpReturn) {
jam();
sendtckeyconf(signal, 0);
return;
@@ -3939,11 +3957,11 @@ Dbtc::lqhKeyConf_checkTransactionState(Signal * signal,
return;
case CS_REC_COMMITTING:
if (TnoOfOutStanding > 0) {
- if (apiConnectPtrP->tckeyrec == ZTCOPCONF_SIZE) {
+ if (regApiPtr.p->tckeyrec == ZTCOPCONF_SIZE) {
jam();
sendtckeyconf(signal, 0);
return;
- } else if (apiConnectPtrP->indexOpReturn) {
+ } else if (regApiPtr.p->indexOpReturn) {
jam();
sendtckeyconf(signal, 0);
return;
@@ -3960,7 +3978,7 @@ Dbtc::lqhKeyConf_checkTransactionState(Signal * signal,
/* CONSISTING OF DIRTY WRITES AND ALL OF THOSE WERE */
/* COMPLETED. ENSURE TCKEYREC IS ZERO TO PREVENT ERRORS. */
/*---------------------------------------------------------------*/
- apiConnectPtrP->tckeyrec = 0;
+ regApiPtr.p->tckeyrec = 0;
return;
default:
TCKEY_abort(signal, 46);
@@ -4218,34 +4236,46 @@ void Dbtc::diverify010Lab(Signal* signal)
jam();
systemErrorLab(signal, __LINE__);
}//if
- if (TfirstfreeApiConnectCopy != RNIL) {
- seizeApiConnectCopy(signal);
- regApiPtr->apiConnectstate = CS_PREPARE_TO_COMMIT;
- /*-----------------------------------------------------------------------
- * WE COME HERE ONLY IF THE TRANSACTION IS PREPARED ON ALL TC CONNECTIONS.
- * THUS WE CAN START THE COMMIT PHASE BY SENDING DIVERIFY ON ALL TC
- * CONNECTIONS AND THEN WHEN ALL DIVERIFYCONF HAVE BEEN RECEIVED THE
- * COMMIT MESSAGE CAN BE SENT TO ALL INVOLVED PARTS.
- *-----------------------------------------------------------------------*/
- EXECUTE_DIRECT(DBDIH, GSN_DIVERIFYREQ, signal, 1);
- if (signal->theData[2] == 0) {
- execDIVERIFYCONF(signal);
+
+ if (regApiPtr->lqhkeyreqrec)
+ {
+ if (TfirstfreeApiConnectCopy != RNIL) {
+ seizeApiConnectCopy(signal);
+ regApiPtr->apiConnectstate = CS_PREPARE_TO_COMMIT;
+ /*-----------------------------------------------------------------------
+ * WE COME HERE ONLY IF THE TRANSACTION IS PREPARED ON ALL TC CONNECTIONS
+ * THUS WE CAN START THE COMMIT PHASE BY SENDING DIVERIFY ON ALL TC
+ * CONNECTIONS AND THEN WHEN ALL DIVERIFYCONF HAVE BEEN RECEIVED THE
+ * COMMIT MESSAGE CAN BE SENT TO ALL INVOLVED PARTS.
+ *---------------------------------------------------------------------*/
+ EXECUTE_DIRECT(DBDIH, GSN_DIVERIFYREQ, signal, 1);
+ if (signal->theData[2] == 0) {
+ execDIVERIFYCONF(signal);
+ }
+ return;
+ } else {
+ /*-----------------------------------------------------------------------
+ * There were no free copy connections available. We must abort the
+ * transaction since otherwise we will have a problem with the report
+ * to the application.
+ * This should more or less not happen but if it happens we do
+ * not want to crash and we do not want to create code to handle it
+ * properly since it is difficult to test it and will be complex to
+ * handle a problem more or less not occurring.
+ *---------------------------------------------------------------------*/
+ terrorCode = ZSEIZE_API_COPY_ERROR;
+ abortErrorLab(signal);
+ return;
}
- return;
- } else {
- /*-----------------------------------------------------------------------
- * There were no free copy connections available. We must abort the
- * transaction since otherwise we will have a problem with the report
- * to the application.
- * This should more or less not happen but if it happens we do not want to
- * crash and we do not want to create code to handle it properly since
- * it is difficult to test it and will be complex to handle a problem
- * more or less not occurring.
- *-----------------------------------------------------------------------*/
- terrorCode = ZSEIZE_API_COPY_ERROR;
- abortErrorLab(signal);
- return;
- }//if
+ }
+ else
+ {
+ jam();
+ sendtckeyconf(signal, 1);
+ regApiPtr->apiConnectstate = CS_CONNECTED;
+ regApiPtr->m_transaction_nodes.clear();
+ setApiConTimer(apiConnectptr.i, 0,__LINE__);
+ }
}//Dbtc::diverify010Lab()
/* ------------------------------------------------------------------------- */
@@ -5093,6 +5123,7 @@ void Dbtc::releaseDirtyWrite(Signal* signal)
void Dbtc::execLQHKEYREF(Signal* signal)
{
const LqhKeyRef * const lqhKeyRef = (LqhKeyRef *)signal->getDataPtr();
+ Uint32 indexId = 0;
jamEntry();
UintR compare_transid1, compare_transid2;
@@ -5144,6 +5175,9 @@ void Dbtc::execLQHKEYREF(Signal* signal)
ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
// The operation executed an index trigger
+ TcIndexData* indexData = c_theIndexes.getPtr(currentIndexId);
+ indexId = indexData->indexId;
+ regApiPtr->errorData = indexId;
const Uint32 opType = regTcPtr->operation;
if (errCode == ZALREADYEXIST)
errCode = terrorCode = ZNOTUNIQUE;
@@ -5156,7 +5190,6 @@ void Dbtc::execLQHKEYREF(Signal* signal)
} else {
jam();
/** ZDELETE && NOT_FOUND */
- TcIndexData* indexData = c_theIndexes.getPtr(currentIndexId);
if(indexData->indexState == IS_BUILDING && state != CS_ABORTING){
jam();
/**
@@ -5241,12 +5274,14 @@ void Dbtc::execLQHKEYREF(Signal* signal)
jam();
regApiPtr->lqhkeyreqrec--; // Compensate for extra during read
tcKeyRef->connectPtr = indexOp;
+ tcKeyRef->errorData = indexId;
EXECUTE_DIRECT(DBTC, GSN_TCKEYREF, signal, TcKeyRef::SignalLength);
apiConnectptr.i = save;
apiConnectptr.p = regApiPtr;
} else {
jam();
tcKeyRef->connectPtr = clientData;
+ tcKeyRef->errorData = indexId;
sendSignal(regApiPtr->ndbapiBlockref,
GSN_TCKEYREF, signal, TcKeyRef::SignalLength, JBB);
}//if
@@ -5260,16 +5295,8 @@ void Dbtc::execLQHKEYREF(Signal* signal)
regApiPtr->lqhkeyreqrec--;
if (regApiPtr->lqhkeyconfrec == regApiPtr->lqhkeyreqrec) {
if (regApiPtr->apiConnectstate == CS_START_COMMITTING) {
- if(regApiPtr->lqhkeyconfrec) {
- jam();
- diverify010Lab(signal);
- } else {
- jam();
- sendtckeyconf(signal, 1);
- regApiPtr->apiConnectstate = CS_CONNECTED;
- regApiPtr->m_transaction_nodes.clear();
- setApiConTimer(apiConnectptr.i, 0,__LINE__);
- }
+ jam();
+ diverify010Lab(signal);
return;
} else if (regApiPtr->tckeyrec > 0 || regApiPtr->m_exec_flag) {
jam();
@@ -6278,7 +6305,7 @@ void Dbtc::timeOutLoopStartLab(Signal* signal, Uint32 api_con_ptr)
jam();
if (api_timer != 0) {
Uint32 error= ZTIME_OUT_ERROR;
- time_out_value= time_out_param + (api_con_ptr & mask_value);
+ time_out_value= time_out_param + (ndb_rand() & mask_value);
if (unlikely(old_mask_value)) // abort during single user mode
{
apiConnectptr.i = api_con_ptr;
@@ -6384,6 +6411,7 @@ void Dbtc::timeOutFoundLab(Signal* signal, Uint32 TapiConPtr, Uint32 errCode)
// conditions should get us here. We ignore it.
/*------------------------------------------------------------------*/
case CS_PREPARE_TO_COMMIT:
+ {
jam();
/*------------------------------------------------------------------*/
/* WE ARE WAITING FOR DIH TO COMMIT THE TRANSACTION. WE SIMPLY*/
@@ -6392,12 +6420,16 @@ void Dbtc::timeOutFoundLab(Signal* signal, Uint32 TapiConPtr, Uint32 errCode)
// To ensure against strange bugs we crash the system if we have passed
// time-out period by a factor of 10 and it is also at least 5 seconds.
/*------------------------------------------------------------------*/
- if (((ctcTimer - getApiConTimer(apiConnectptr.i)) > (10 * ctimeOutValue)) &&
- ((ctcTimer - getApiConTimer(apiConnectptr.i)) > 500)) {
- jam();
- systemErrorLab(signal, __LINE__);
+ Uint32 time_passed = ctcTimer - getApiConTimer(apiConnectptr.i);
+ if (time_passed > 500 &&
+ time_passed > (5 * cDbHbInterval) &&
+ time_passed > (10 * ctimeOutValue))
+ {
+ jam();
+ systemErrorLab(signal, __LINE__);
}//if
break;
+ }
case CS_COMMIT_SENT:
jam();
/*------------------------------------------------------------------*/
@@ -6481,6 +6513,7 @@ void Dbtc::timeOutFoundLab(Signal* signal, Uint32 TapiConPtr, Uint32 errCode)
return;
case CS_WAIT_COMMIT_CONF:
jam();
+ CRASH_INSERTION(8053);
tcConnectptr.i = apiConnectptr.p->currentTcConnect;
ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
arrGuard(apiConnectptr.p->currentReplicaNo, MAX_REPLICAS);
@@ -10554,6 +10587,7 @@ void Dbtc::releaseAbortResources(Signal* signal)
tcRollbackRep->transId[0] = apiConnectptr.p->transid[0];
tcRollbackRep->transId[1] = apiConnectptr.p->transid[1];
tcRollbackRep->returnCode = apiConnectptr.p->returncode;
+ tcRollbackRep->errorData = apiConnectptr.p->errorData;
sendSignal(blockRef, GSN_TCROLLBACKREP, signal,
TcRollbackRep::SignalLength, JBB);
}
@@ -11978,6 +12012,7 @@ void Dbtc::execTCKEYCONF(Signal* signal)
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4349;
+ tcIndxRef->errorData = 0;
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
return;
@@ -11997,6 +12032,7 @@ void Dbtc::execTCKEYCONF(Signal* signal)
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4349;
+ tcIndxRef->errorData = 0;
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
return;
@@ -12080,6 +12116,7 @@ void Dbtc::execTCKEYREF(Signal* signal)
tcIndxRef->transId[0] = tcKeyRef->transId[0];
tcIndxRef->transId[1] = tcKeyRef->transId[1];
tcIndxRef->errorCode = tcKeyRef->errorCode;
+ tcIndxRef->errorData = 0;
releaseIndexOperation(regApiPtr, indexOp);
@@ -12157,6 +12194,7 @@ void Dbtc::execTRANSID_AI(Signal* signal)
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4000;
+ tcIndxRef->errorData = 0;
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
return;
@@ -12172,6 +12210,7 @@ void Dbtc::execTRANSID_AI(Signal* signal)
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4349;
+ tcIndxRef->errorData = 0;
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
return;
@@ -12200,6 +12239,7 @@ void Dbtc::execTRANSID_AI(Signal* signal)
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4349;
+ tcIndxRef->errorData = 0;
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
*/
@@ -12225,6 +12265,7 @@ void Dbtc::execTRANSID_AI(Signal* signal)
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4349;
+ tcIndxRef->errorData = regApiPtr->errorData;
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
return;
@@ -12278,6 +12319,7 @@ void Dbtc::readIndexTable(Signal* signal,
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4000;
+ // tcIndxRef->errorData = ??; Where to find indexId
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
return;
@@ -12424,6 +12466,7 @@ void Dbtc::executeIndexOperation(Signal* signal,
tcIndxRef->transId[0] = regApiPtr->transid[0];
tcIndxRef->transId[1] = regApiPtr->transid[1];
tcIndxRef->errorCode = 4349;
+ tcIndxRef->errorData = 0;
sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
TcKeyRef::SignalLength, JBB);
return;
diff --git a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
index f28687dca0d..45d124b8d7d 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
@@ -518,6 +518,7 @@ typedef Ptr<Fragoperrec> FragoperrecPtr;
Uint32 m_savePointId;
Uint32 m_scanGCI;
};
+ Uint32 m_endPage;
// lock waited for or obtained and not yet passed to LQH
Uint32 m_accLockOp;
@@ -641,6 +642,8 @@ typedef Ptr<Fragoperrec> FragoperrecPtr;
*/
Page_request_list::Head m_page_requests[MAX_FREE_LIST];
+ DLList<Page>::Head m_unmap_pages;
+
/**
* Current extent
*/
@@ -702,7 +705,8 @@ struct Fragrecord {
DLList<ScanOp>::Head m_scanList;
- enum { UC_LCP = 1, UC_CREATE = 2 };
+ enum { UC_LCP = 1, UC_CREATE = 2, UC_SET_LCP = 3 };
+ Uint32 m_restore_lcp_id;
Uint32 m_undo_complete;
Uint32 m_tablespace_id;
Uint32 m_logfile_group_id;
@@ -1573,6 +1577,8 @@ public:
void nr_delete_page_callback(Signal*, Uint32 op, Uint32 page);
void nr_delete_log_buffer_callback(Signal*, Uint32 op, Uint32 page);
+
+ bool get_frag_info(Uint32 tableId, Uint32 fragId, Uint32* maxPage);
private:
BLOCK_DEFINES(Dbtup);
@@ -2830,7 +2836,7 @@ private:
public:
int disk_page_load_hook(Uint32 page_id);
- void disk_page_unmap_callback(Uint32 page_id, Uint32 dirty_count);
+ void disk_page_unmap_callback(Uint32 when, Uint32 page, Uint32 dirty_count);
int disk_restart_alloc_extent(Uint32 tableId, Uint32 fragId,
const Local_key* key, Uint32 pages);
@@ -2851,11 +2857,11 @@ public:
Local_key m_key;
};
- void disk_restart_mark_no_lcp(Uint32 table, Uint32 frag);
+ void disk_restart_lcp_id(Uint32 table, Uint32 frag, Uint32 lcpId);
private:
void disk_restart_undo_next(Signal*);
- void disk_restart_undo_lcp(Uint32, Uint32, Uint32 flag);
+ void disk_restart_undo_lcp(Uint32, Uint32, Uint32 flag, Uint32 lcpId);
void disk_restart_undo_callback(Signal* signal, Uint32, Uint32);
void disk_restart_undo_alloc(Apply_undo*);
void disk_restart_undo_update(Apply_undo*);
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp
index 87705232de2..8420e7f2bde 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp
@@ -903,8 +903,10 @@ Dbtup::disk_page_set_dirty(PagePtr pagePtr)
}
void
-Dbtup::disk_page_unmap_callback(Uint32 page_id, Uint32 dirty_count)
+Dbtup::disk_page_unmap_callback(Uint32 when,
+ Uint32 page_id, Uint32 dirty_count)
{
+ jamEntry();
Ptr<GlobalPage> gpage;
m_global_page_pool.getPtr(gpage, page_id);
PagePtr pagePtr;
@@ -918,17 +920,9 @@ Dbtup::disk_page_unmap_callback(Uint32 page_id, Uint32 dirty_count)
{
return ;
}
-
- Local_key key;
- key.m_page_no = pagePtr.p->m_page_no;
- key.m_file_no = pagePtr.p->m_file_no;
- Uint32 idx = pagePtr.p->list_index;
- ndbassert((idx & 0x8000) == 0);
+ Uint32 idx = pagePtr.p->list_index;
- if (DBG_DISK)
- ndbout << "disk_page_unmap_callback " << key << endl;
-
Ptr<Tablerec> tabPtr;
tabPtr.i= pagePtr.p->m_table_id;
ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
@@ -938,26 +932,89 @@ Dbtup::disk_page_unmap_callback(Uint32 page_id, Uint32 dirty_count)
Disk_alloc_info& alloc= fragPtr.p->m_disk_alloc_info;
- if (dirty_count == 0)
+ if (when == 0)
{
- Uint32 free = pagePtr.p->free_space;
- Uint32 used = pagePtr.p->uncommitted_used_space;
- ddassert(free >= used);
- ddassert(alloc.calc_page_free_bits(free - used) == idx);
+ /**
+ * Before pageout
+ */
+ jam();
+
+ if (DBG_DISK)
+ {
+ Local_key key;
+ key.m_page_no = pagePtr.p->m_page_no;
+ key.m_file_no = pagePtr.p->m_file_no;
+ ndbout << "disk_page_unmap_callback(before) " << key
+ << " cnt: " << dirty_count << " " << (idx & ~0x8000) << endl;
+ }
+
+ ndbassert((idx & 0x8000) == 0);
+
+ ArrayPool<Page> *pool= (ArrayPool<Page>*)&m_global_page_pool;
+ LocalDLList<Page> list(*pool, alloc.m_dirty_pages[idx]);
+ LocalDLList<Page> list2(*pool, alloc.m_unmap_pages);
+ list.remove(pagePtr);
+ list2.add(pagePtr);
+
+ if (dirty_count == 0)
+ {
+ jam();
+ pagePtr.p->list_index = idx | 0x8000;
+
+ Local_key key;
+ key.m_page_no = pagePtr.p->m_page_no;
+ key.m_file_no = pagePtr.p->m_file_no;
+
+ Uint32 free = pagePtr.p->free_space;
+ Uint32 used = pagePtr.p->uncommitted_used_space;
+ ddassert(free >= used);
+ ddassert(alloc.calc_page_free_bits(free - used) == idx);
+
+ Tablespace_client tsman(0, c_tsman,
+ fragPtr.p->fragTableId,
+ fragPtr.p->fragmentId,
+ fragPtr.p->m_tablespace_id);
+
+ tsman.unmap_page(&key, idx);
+ jamEntry();
+ }
+ }
+ else if (when == 1)
+ {
+ /**
+ * After page out
+ */
+ jam();
+
+ Local_key key;
+ key.m_page_no = pagePtr.p->m_page_no;
+ key.m_file_no = pagePtr.p->m_file_no;
+ Uint32 real_free = pagePtr.p->free_space;
+ if (DBG_DISK)
+ {
+ ndbout << "disk_page_unmap_callback(after) " << key
+ << " cnt: " << dirty_count << " " << (idx & ~0x8000) << endl;
+ }
+
+ ArrayPool<Page> *pool= (ArrayPool<Page>*)&m_global_page_pool;
+ LocalDLList<Page> list(*pool, alloc.m_unmap_pages);
+ list.remove(pagePtr);
+
Tablespace_client tsman(0, c_tsman,
fragPtr.p->fragTableId,
fragPtr.p->fragmentId,
fragPtr.p->m_tablespace_id);
- tsman.unmap_page(&key, idx);
- jamEntry();
- pagePtr.p->list_index = idx | 0x8000;
+ if (DBG_DISK && alloc.calc_page_free_bits(real_free) != (idx & ~0x8000))
+ {
+ ndbout << key
+ << " calc: " << alloc.calc_page_free_bits(real_free)
+ << " idx: " << (idx & ~0x8000)
+ << endl;
+ }
+ tsman.update_page_free_bits(&key, alloc.calc_page_free_bits(real_free));
}
-
- ArrayPool<Page> *pool= (ArrayPool<Page>*)&m_global_page_pool;
- LocalDLList<Page> list(*pool, alloc.m_dirty_pages[idx]);
- list.remove(pagePtr);
}
void
@@ -969,8 +1026,6 @@ Dbtup::disk_page_alloc(Signal* signal,
Disk_alloc_info& alloc= fragPtrP->m_disk_alloc_info;
Uint64 lsn;
- Uint32 old_free = pagePtr.p->free_space;
- Uint32 old_bits= alloc.calc_page_free_bits(old_free);
if (tabPtrP->m_attributes[DD].m_no_of_varsize == 0)
{
ddassert(pagePtr.p->uncommitted_used_space > 0);
@@ -988,20 +1043,6 @@ Dbtup::disk_page_alloc(Signal* signal,
lsn= disk_page_undo_alloc(pagePtr.p, key, sz, gci, logfile_group_id);
}
-
- Uint32 new_free = pagePtr.p->free_space;
- Uint32 new_bits= alloc.calc_page_free_bits(new_free);
-
- if (old_bits != new_bits)
- {
- Tablespace_client tsman(signal, c_tsman,
- fragPtrP->fragTableId,
- fragPtrP->fragmentId,
- fragPtrP->m_tablespace_id);
-
- tsman.update_page_free_bits(key, new_bits, lsn);
- jamEntry();
- }
}
void
@@ -1016,7 +1057,6 @@ Dbtup::disk_page_free(Signal *signal,
Uint32 logfile_group_id= fragPtrP->m_logfile_group_id;
Disk_alloc_info& alloc= fragPtrP->m_disk_alloc_info;
Uint32 old_free= pagePtr.p->free_space;
- Uint32 old_bits= alloc.calc_page_free_bits(old_free);
Uint32 sz;
Uint64 lsn;
@@ -1043,19 +1083,7 @@ Dbtup::disk_page_free(Signal *signal,
}
Uint32 new_free = pagePtr.p->free_space;
- Uint32 new_bits = alloc.calc_page_free_bits(new_free);
- if (old_bits != new_bits)
- {
- Tablespace_client tsman(signal, c_tsman,
- fragPtrP->fragTableId,
- fragPtrP->fragmentId,
- fragPtrP->m_tablespace_id);
-
- tsman.update_page_free_bits(key, new_bits, lsn);
- jamEntry();
- }
-
Uint32 ext = pagePtr.p->m_extent_info_ptr;
Uint32 used = pagePtr.p->uncommitted_used_space;
Uint32 old_idx = pagePtr.p->list_index;
@@ -1341,15 +1369,23 @@ Dbtup::disk_restart_undo(Signal* signal, Uint64 lsn,
case File_formats::Undofile::UNDO_LCP_FIRST:
case File_formats::Undofile::UNDO_LCP:
{
+ jam();
ndbrequire(len == 3);
+ Uint32 lcp = ptr[0];
Uint32 tableId = ptr[1] >> 16;
Uint32 fragId = ptr[1] & 0xFFFF;
- disk_restart_undo_lcp(tableId, fragId, Fragrecord::UC_LCP);
+ disk_restart_undo_lcp(tableId, fragId, Fragrecord::UC_LCP, lcp);
disk_restart_undo_next(signal);
+
+ if (DBG_UNDO)
+ {
+ ndbout_c("UNDO LCP %u (%u, %u)", lcp, tableId, fragId);
+ }
return;
}
case File_formats::Undofile::UNDO_TUP_ALLOC:
{
+ jam();
Disk_undo::Alloc* rec= (Disk_undo::Alloc*)ptr;
preq.m_page.m_page_no = rec->m_page_no;
preq.m_page.m_file_no = rec->m_file_no_page_idx >> 16;
@@ -1358,6 +1394,7 @@ Dbtup::disk_restart_undo(Signal* signal, Uint64 lsn,
}
case File_formats::Undofile::UNDO_TUP_UPDATE:
{
+ jam();
Disk_undo::Update* rec= (Disk_undo::Update*)ptr;
preq.m_page.m_page_no = rec->m_page_no;
preq.m_page.m_file_no = rec->m_file_no_page_idx >> 16;
@@ -1366,6 +1403,7 @@ Dbtup::disk_restart_undo(Signal* signal, Uint64 lsn,
}
case File_formats::Undofile::UNDO_TUP_FREE:
{
+ jam();
Disk_undo::Free* rec= (Disk_undo::Free*)ptr;
preq.m_page.m_page_no = rec->m_page_no;
preq.m_page.m_file_no = rec->m_file_no_page_idx >> 16;
@@ -1377,6 +1415,7 @@ Dbtup::disk_restart_undo(Signal* signal, Uint64 lsn,
*
*/
{
+ jam();
Disk_undo::Create* rec= (Disk_undo::Create*)ptr;
Ptr<Tablerec> tabPtr;
tabPtr.i= rec->m_table;
@@ -1384,12 +1423,34 @@ Dbtup::disk_restart_undo(Signal* signal, Uint64 lsn,
for(Uint32 i = 0; i<MAX_FRAG_PER_NODE; i++)
if (tabPtr.p->fragrec[i] != RNIL)
disk_restart_undo_lcp(tabPtr.i, tabPtr.p->fragid[i],
- Fragrecord::UC_CREATE);
+ Fragrecord::UC_CREATE, 0);
disk_restart_undo_next(signal);
+
+ if (DBG_UNDO)
+ {
+ ndbout_c("UNDO CREATE (%u)", tabPtr.i);
+ }
return;
}
case File_formats::Undofile::UNDO_TUP_DROP:
+ {
jam();
+ Disk_undo::Drop* rec = (Disk_undo::Drop*)ptr;
+ Ptr<Tablerec> tabPtr;
+ tabPtr.i= rec->m_table;
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+ for(Uint32 i = 0; i<MAX_FRAG_PER_NODE; i++)
+ if (tabPtr.p->fragrec[i] != RNIL)
+ disk_restart_undo_lcp(tabPtr.i, tabPtr.p->fragid[i],
+ Fragrecord::UC_CREATE, 0);
+ disk_restart_undo_next(signal);
+
+ if (DBG_UNDO)
+ {
+ ndbout_c("UNDO DROP (%u)", tabPtr.i);
+ }
+ return;
+ }
case File_formats::Undofile::UNDO_TUP_ALLOC_EXTENT:
jam();
case File_formats::Undofile::UNDO_TUP_FREE_EXTENT:
@@ -1398,6 +1459,7 @@ Dbtup::disk_restart_undo(Signal* signal, Uint64 lsn,
return;
case File_formats::Undofile::UNDO_END:
+ jam();
f_undo_done = true;
return;
default:
@@ -1431,14 +1493,32 @@ Dbtup::disk_restart_undo_next(Signal* signal)
}
void
-Dbtup::disk_restart_mark_no_lcp(Uint32 tableId, Uint32 fragId)
+Dbtup::disk_restart_lcp_id(Uint32 tableId, Uint32 fragId, Uint32 lcpId)
{
jamEntry();
- disk_restart_undo_lcp(tableId, fragId, Fragrecord::UC_CREATE);
+
+ if (lcpId == RNIL)
+ {
+ disk_restart_undo_lcp(tableId, fragId, Fragrecord::UC_CREATE, 0);
+ if (DBG_UNDO)
+ {
+ ndbout_c("mark_no_lcp (%u, %u)", tableId, fragId);
+ }
+ }
+ else
+ {
+ disk_restart_undo_lcp(tableId, fragId, Fragrecord::UC_SET_LCP, lcpId);
+ if (DBG_UNDO)
+ {
+ ndbout_c("mark_no_lcp (%u, %u)", tableId, fragId);
+ }
+
+ }
}
void
-Dbtup::disk_restart_undo_lcp(Uint32 tableId, Uint32 fragId, Uint32 flag)
+Dbtup::disk_restart_undo_lcp(Uint32 tableId, Uint32 fragId, Uint32 flag,
+ Uint32 lcpId)
{
Ptr<Tablerec> tabPtr;
tabPtr.i= tableId;
@@ -1446,11 +1526,43 @@ Dbtup::disk_restart_undo_lcp(Uint32 tableId, Uint32 fragId, Uint32 flag)
if (tabPtr.p->tableStatus == DEFINED)
{
+ jam();
FragrecordPtr fragPtr;
getFragmentrec(fragPtr, fragId, tabPtr.p);
if (!fragPtr.isNull())
{
- fragPtr.p->m_undo_complete |= flag;
+ jam();
+ switch(flag){
+ case Fragrecord::UC_CREATE:
+ jam();
+ fragPtr.p->m_undo_complete |= flag;
+ return;
+ case Fragrecord::UC_LCP:
+ jam();
+ if (fragPtr.p->m_undo_complete == 0 &&
+ fragPtr.p->m_restore_lcp_id == lcpId)
+ {
+ jam();
+ fragPtr.p->m_undo_complete |= flag;
+ if (DBG_UNDO)
+ ndbout_c("table: %u fragment: %u lcp: %u -> done",
+ tableId, fragId, lcpId);
+ }
+ return;
+ case Fragrecord::UC_SET_LCP:
+ {
+ jam();
+ if (DBG_UNDO)
+ ndbout_c("table: %u fragment: %u restore to lcp: %u",
+ tableId, fragId, lcpId);
+ ndbrequire(fragPtr.p->m_undo_complete == 0);
+ ndbrequire(fragPtr.p->m_restore_lcp_id == RNIL);
+ fragPtr.p->m_restore_lcp_id = lcpId;
+ return;
+ }
+ }
+ jamLine(flag);
+ ndbrequire(false);
}
}
}
@@ -1474,6 +1586,7 @@ Dbtup::disk_restart_undo_callback(Signal* signal,
pagePtr.p->nextList != RNIL ||
pagePtr.p->prevList != RNIL)
{
+ jam();
update = true;
pagePtr.p->list_index |= 0x8000;
pagePtr.p->nextList = pagePtr.p->prevList = RNIL;
@@ -1484,6 +1597,9 @@ Dbtup::disk_restart_undo_callback(Signal* signal,
if (tableId >= cnoOfTablerec)
{
+ jam();
+ if (DBG_UNDO)
+ ndbout_c("UNDO table> %u", tableId);
disk_restart_undo_next(signal);
return;
}
@@ -1492,6 +1608,9 @@ Dbtup::disk_restart_undo_callback(Signal* signal,
if (undo->m_table_ptr.p->tableStatus != DEFINED)
{
+ jam();
+ if (DBG_UNDO)
+ ndbout_c("UNDO !defined (%u) ", tableId);
disk_restart_undo_next(signal);
return;
}
@@ -1499,19 +1618,25 @@ Dbtup::disk_restart_undo_callback(Signal* signal,
getFragmentrec(undo->m_fragment_ptr, fragId, undo->m_table_ptr.p);
if(undo->m_fragment_ptr.isNull())
{
+ jam();
+ if (DBG_UNDO)
+ ndbout_c("UNDO fragment null %u/%u", tableId, fragId);
disk_restart_undo_next(signal);
return;
}
if (undo->m_fragment_ptr.p->m_undo_complete)
{
+ jam();
+ if (DBG_UNDO)
+ ndbout_c("UNDO undo complete %u/%u", tableId, fragId);
disk_restart_undo_next(signal);
return;
}
- Local_key key;
- key.m_page_no = pagePtr.p->m_page_no;
- key.m_file_no = pagePtr.p->m_file_no;
+ Local_key key = undo->m_key;
+// key.m_page_no = pagePtr.p->m_page_no;
+// key.m_file_no = pagePtr.p->m_file_no;
Uint64 lsn = 0;
lsn += pagePtr.p->m_page_header.m_page_lsn_hi; lsn <<= 32;
@@ -1521,6 +1646,7 @@ Dbtup::disk_restart_undo_callback(Signal* signal,
if (undo->m_lsn <= lsn)
{
+ jam();
if (DBG_UNDO)
{
ndbout << "apply: " << undo->m_lsn << "(" << lsn << " )"
@@ -1535,12 +1661,15 @@ Dbtup::disk_restart_undo_callback(Signal* signal,
*/
switch(undo->m_type){
case File_formats::Undofile::UNDO_TUP_ALLOC:
+ jam();
disk_restart_undo_alloc(undo);
break;
case File_formats::Undofile::UNDO_TUP_UPDATE:
+ jam();
disk_restart_undo_update(undo);
break;
case File_formats::Undofile::UNDO_TUP_FREE:
+ jam();
disk_restart_undo_free(undo);
break;
default:
@@ -1555,14 +1684,17 @@ Dbtup::disk_restart_undo_callback(Signal* signal,
m_pgman.update_lsn(undo->m_key, lsn);
jamEntry();
+
+ disk_restart_undo_page_bits(signal, undo);
}
else if (DBG_UNDO)
{
+ jam();
ndbout << "ignore: " << undo->m_lsn << "(" << lsn << " )"
- << key << " type: " << undo->m_type << endl;
+ << key << " type: " << undo->m_type
+ << " tab: " << tableId << endl;
}
- disk_restart_undo_page_bits(signal, undo);
disk_restart_undo_next(signal);
}
@@ -1637,16 +1769,12 @@ Dbtup::disk_restart_undo_page_bits(Signal* signal, Apply_undo* undo)
Uint32 new_bits = alloc.calc_page_free_bits(free);
pageP->list_index = 0x8000 | new_bits;
- Uint64 lsn = 0;
- lsn += pageP->m_page_header.m_page_lsn_hi; lsn <<= 32;
- lsn += pageP->m_page_header.m_page_lsn_lo;
-
Tablespace_client tsman(signal, c_tsman,
fragPtrP->fragTableId,
fragPtrP->fragmentId,
fragPtrP->m_tablespace_id);
- tsman.restart_undo_page_free_bits(&undo->m_key, new_bits, undo->m_lsn, lsn);
+ tsman.restart_undo_page_free_bits(&undo->m_key, new_bits);
jamEntry();
}
@@ -1683,6 +1811,7 @@ Dbtup::disk_restart_alloc_extent(Uint32 tableId, Uint32 fragId,
if (alloc.m_curr_extent_info_ptr_i != RNIL)
{
+ jam();
Ptr<Extent_info> old;
c_extent_pool.getPtr(old, alloc.m_curr_extent_info_ptr_i);
ndbassert(old.p->m_free_matrix_pos == RNIL);
@@ -1709,6 +1838,7 @@ void
Dbtup::disk_restart_page_bits(Uint32 tableId, Uint32 fragId,
const Local_key*, Uint32 bits)
{
+ jam();
TablerecPtr tabPtr;
FragrecordPtr fragPtr;
tabPtr.i = tableId;
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
index 45766e5e9c4..a642d704eb9 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
@@ -1957,9 +1957,8 @@ int Dbtup::interpreterNextLab(Signal* signal,
Uint32 TdataForUpdate[3];
Uint32 Tlen;
- AttributeHeader& ah= AttributeHeader::init(&TdataForUpdate[0],
- TattrId,
- TattrNoOfWords << 2);
+ AttributeHeader ah(TattrId, TattrNoOfWords << 2);
+ TdataForUpdate[0]= ah.m_value;
TdataForUpdate[1]= TregMemBuffer[theRegister + 2];
TdataForUpdate[2]= TregMemBuffer[theRegister + 3];
Tlen= TattrNoOfWords + 1;
@@ -1975,6 +1974,7 @@ int Dbtup::interpreterNextLab(Signal* signal,
// Write a NULL value into the attribute
/* --------------------------------------------------------- */
ah.setNULL();
+ TdataForUpdate[0]= ah.m_value;
Tlen= 1;
}
int TnoDataRW= updateAttributes(req_struct,
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
index 6866236f15e..176efac8058 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
@@ -143,6 +143,7 @@ void Dbtup::execTUPFRAGREQ(Signal* signal)
regFragPtr.p->m_lcp_scan_op = RNIL;
regFragPtr.p->m_lcp_keep_list = RNIL;
regFragPtr.p->m_var_page_chunks = RNIL;
+ regFragPtr.p->m_restore_lcp_id = RNIL;
if (ERROR_INSERTED(4007) && regTabPtr.p->fragid[0] == fragId ||
ERROR_INSERTED(4008) && regTabPtr.p->fragid[1] == fragId) {
@@ -674,11 +675,11 @@ Dbtup::undo_createtable_callback(Signal* signal, Uint32 opPtrI, Uint32 unused)
switch(ret){
case 0:
return;
+ case -1:
+ warningEvent("Failed to sync log for create of table: %u", regTabPtr.i);
default:
- ndbout_c("ret: %d", ret);
- ndbrequire(false);
+ execute(signal, req.m_callback, regFragPtr.p->m_logfile_group_id);
}
-
}
void
@@ -959,8 +960,6 @@ void Dbtup::releaseFragment(Signal* signal, Uint32 tableId,
return;
}
-#if NOT_YET_UNDO_DROP_TABLE
-#error "This code is complete, but I prefer not to enable it until I need it"
if (logfile_group_id != RNIL)
{
Callback cb;
@@ -968,8 +967,15 @@ void Dbtup::releaseFragment(Signal* signal, Uint32 tableId,
cb.m_callbackFunction =
safe_cast(&Dbtup::drop_table_log_buffer_callback);
Uint32 sz= sizeof(Disk_undo::Drop) >> 2;
- (void) c_lgman->alloc_log_space(logfile_group_id, sz);
-
+ int r0 = c_lgman->alloc_log_space(logfile_group_id, sz);
+ if (r0)
+ {
+ jam();
+ warningEvent("Failed to alloc log space for drop table: %u",
+ tabPtr.i);
+ goto done;
+ }
+
Logfile_client lgman(this, c_lgman, logfile_group_id);
int res= lgman.get_log_buffer(signal, sz, &cb);
switch(res){
@@ -977,15 +983,18 @@ void Dbtup::releaseFragment(Signal* signal, Uint32 tableId,
jam();
return;
case -1:
- ndbrequire("NOT YET IMPLEMENTED" == 0);
+ warningEvent("Failed to get log buffer for drop table: %u",
+ tabPtr.i);
+ c_lgman->free_log_space(logfile_group_id, sz);
+ goto done;
break;
default:
execute(signal, cb, logfile_group_id);
return;
}
}
-#endif
-
+
+done:
drop_table_logsync_callback(signal, tabPtr.i, RNIL);
}
@@ -997,7 +1006,20 @@ Dbtup::drop_fragment_unmap_pages(Signal *signal,
{
if (tabPtr.p->m_no_of_disk_attributes)
{
+ jam();
Disk_alloc_info& alloc_info= fragPtr.p->m_disk_alloc_info;
+
+ if (!alloc_info.m_unmap_pages.isEmpty())
+ {
+ jam();
+ ndbout_c("waiting for unmape pages");
+ signal->theData[0] = ZUNMAP_PAGES;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = fragPtr.i;
+ signal->theData[3] = pos;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+ return;
+ }
while(alloc_info.m_dirty_pages[pos].isEmpty() && pos < MAX_FREE_LIST)
pos++;
@@ -1164,9 +1186,10 @@ Dbtup::drop_table_log_buffer_callback(Signal* signal, Uint32 tablePtrI,
switch(ret){
case 0:
return;
+ case -1:
+ warningEvent("Failed to syn log for drop of table: %u", tablePtrI);
default:
- ndbout_c("ret: %d", ret);
- ndbrequire(false);
+ execute(signal, req.m_callback, logfile_group_id);
}
}
@@ -1441,3 +1464,22 @@ Dbtup::complete_restore_lcp(Uint32 tableId, Uint32 fragId)
tabDesc += 2;
}
}
+
+bool
+Dbtup::get_frag_info(Uint32 tableId, Uint32 fragId, Uint32* maxPage)
+{
+ jamEntry();
+ TablerecPtr tabPtr;
+ tabPtr.i= tableId;
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+
+ FragrecordPtr fragPtr;
+ getFragmentrec(fragPtr, fragId, tabPtr.p);
+
+ if (maxPage)
+ {
+ * maxPage = fragPtr.p->noOfPages;
+ }
+
+ return true;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp
index b0b0cec6b76..64f81dc11ab 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp
@@ -818,9 +818,7 @@ Dbtup::checkUpdateOfPrimaryKey(KeyReqStruct* req_struct,
Tablerec* const regTabPtr)
{
Uint32 keyReadBuffer[MAX_KEY_SIZE_IN_WORDS];
- Uint32 attributeHeader;
TableDescriptor* attr_descr = req_struct->attr_descr;
- AttributeHeader* ahOut = (AttributeHeader*)&attributeHeader;
AttributeHeader ahIn(*updateBuffer);
Uint32 attributeId = ahIn.getAttributeId();
Uint32 attrDescriptorIndex = attributeId << ZAD_LOG_SIZE;
@@ -843,7 +841,7 @@ Dbtup::checkUpdateOfPrimaryKey(KeyReqStruct* req_struct,
ReadFunction f = regTabPtr->readFunctionArray[attributeId];
- AttributeHeader::init(&attributeHeader, attributeId, 0);
+ AttributeHeader attributeHeader(attributeId, 0);
req_struct->out_buf_index = 0;
req_struct->max_read = MAX_KEY_SIZE_IN_WORDS;
req_struct->attr_descriptor = attrDescriptor;
@@ -852,12 +850,12 @@ Dbtup::checkUpdateOfPrimaryKey(KeyReqStruct* req_struct,
req_struct->xfrm_flag = true;
ndbrequire((this->*f)(&keyReadBuffer[0],
req_struct,
- ahOut,
+ &attributeHeader,
attributeOffset));
req_struct->xfrm_flag = tmp;
- ndbrequire(req_struct->out_buf_index == ahOut->getDataSize());
- if (ahIn.getDataSize() != ahOut->getDataSize()) {
+ ndbrequire(req_struct->out_buf_index == attributeHeader.getDataSize());
+ if (ahIn.getDataSize() != attributeHeader.getDataSize()) {
jam();
return true;
}
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp
index 6e53531e118..00c8f108234 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp
@@ -95,7 +95,23 @@ Dbtup::execACC_SCANREQ(Signal* signal)
}
}
- bits |= AccScanReq::getNRScanFlag(req->requestInfo) ? ScanOp::SCAN_NR : 0;
+ if (AccScanReq::getNRScanFlag(req->requestInfo))
+ {
+ jam();
+ bits |= ScanOp::SCAN_NR;
+ scanPtr.p->m_endPage = req->maxPage;
+ if (req->maxPage != RNIL && req->maxPage > frag.noOfPages)
+ {
+ ndbout_c("%u %u endPage: %u (noOfPages: %u)",
+ tablePtr.i, fragId,
+ req->maxPage, fragPtr.p->noOfPages);
+ }
+ }
+ else
+ {
+ jam();
+ scanPtr.p->m_endPage = RNIL;
+ }
// set up scan op
new (scanPtr.p) ScanOp();
@@ -540,7 +556,7 @@ Dbtup::scanFirst(Signal*, ScanOpPtr scanPtr)
ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
Fragrecord& frag = *fragPtr.p;
// in the future should not pre-allocate pages
- if (frag.noOfPages == 0) {
+ if (frag.noOfPages == 0 && ((bits & ScanOp::SCAN_NR) == 0)) {
jam();
scan.m_state = ScanOp::Last;
return;
@@ -602,6 +618,24 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
if (lcp && lcp_list != RNIL)
goto found_lcp_keep;
+
+ switch(pos.m_get){
+ case ScanPos::Get_next_tuple:
+ case ScanPos::Get_next_tuple_fs:
+ jam();
+ key.m_page_idx += size;
+ // fall through
+ case ScanPos::Get_tuple:
+ case ScanPos::Get_tuple_fs:
+ jam();
+ /**
+ * We need to refetch page after timeslice
+ */
+ pos.m_get = ScanPos::Get_page;
+ break;
+ default:
+ break;
+ }
while (true) {
switch (pos.m_get) {
@@ -632,11 +666,23 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
key.m_page_no++;
if (key.m_page_no >= frag.noOfPages) {
jam();
+
+ if ((bits & ScanOp::SCAN_NR) && (scan.m_endPage != RNIL))
+ {
+ jam();
+ if (key.m_page_no < scan.m_endPage)
+ {
+ jam();
+ ndbout_c("scanning page %u", key.m_page_no);
+ goto cont;
+ }
+ }
// no more pages, scan ends
pos.m_get = ScanPos::Get_undef;
scan.m_state = ScanOp::Last;
return true;
}
+ cont:
key.m_page_idx = 0;
pos.m_get = ScanPos::Get_page_mm;
// clear cached value
@@ -649,7 +695,13 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
{
if (pos.m_realpid_mm == RNIL) {
jam();
- pos.m_realpid_mm = getRealpid(fragPtr.p, key.m_page_no);
+ if (key.m_page_no < frag.noOfPages)
+ pos.m_realpid_mm = getRealpid(fragPtr.p, key.m_page_no);
+ else
+ {
+ ndbassert(bits & ScanOp::SCAN_NR);
+ goto nopage;
+ }
}
PagePtr pagePtr;
c_page_pool.getPtr(pagePtr, pos.m_realpid_mm);
@@ -657,9 +709,18 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
if (pagePtr.p->page_state == ZEMPTY_MM) {
// skip empty page
jam();
- pos.m_get = ScanPos::Get_next_page_mm;
- break; // incr loop count
+ if (! (bits & ScanOp::SCAN_NR))
+ {
+ pos.m_get = ScanPos::Get_next_page_mm;
+ break; // incr loop count
+ }
+ else
+ {
+ jam();
+ pos.m_realpid_mm = RNIL;
+ }
}
+ nopage:
pos.m_page = pagePtr.p;
pos.m_get = ScanPos::Get_tuple;
}
@@ -772,7 +833,7 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
uncommitted = committed = ~(unsigned)0;
int ret = tsman.get_page_free_bits(&key, &uncommitted, &committed);
ndbrequire(ret == 0);
- if (committed == 0) {
+ if (committed == 0 && uncommitted == 0) {
// skip empty page
jam();
pos.m_get = ScanPos::Get_next_page_dd;
@@ -820,11 +881,11 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
{
pos.m_get = ScanPos::Get_next_tuple_fs;
th = (Tuple_header*)&page->m_data[key.m_page_idx];
- thbits = th->m_header_bits;
if (likely(! (bits & ScanOp::SCAN_NR)))
{
jam();
+ thbits = th->m_header_bits;
if (! (thbits & Tuple_header::FREE))
{
goto found_tuple;
@@ -832,7 +893,15 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
}
else
{
- if ((foundGCI = *th->get_mm_gci(tablePtr.p)) > scanGCI)
+ if (pos.m_realpid_mm == RNIL)
+ {
+ jam();
+ foundGCI = 0;
+ goto found_deleted_rowid;
+ }
+ thbits = th->m_header_bits;
+ if ((foundGCI = *th->get_mm_gci(tablePtr.p)) > scanGCI ||
+ foundGCI == 0)
{
if (! (thbits & Tuple_header::FREE))
{
@@ -904,7 +973,8 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
Fix_page *mmpage = (Fix_page*)c_page_pool.getPtr(pos.m_realpid_mm);
th = (Tuple_header*)(mmpage->m_data + key_mm.m_page_idx);
- if ((foundGCI = *th->get_mm_gci(tablePtr.p)) > scanGCI)
+ if ((foundGCI = *th->get_mm_gci(tablePtr.p)) > scanGCI ||
+ foundGCI == 0)
{
if (! (thbits & Tuple_header::FREE))
break;
diff --git a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp
index 550e255061f..f34bc41af06 100644
--- a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp
+++ b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp
@@ -1169,9 +1169,7 @@ DbUtil::prepareOperation(Signal* signal, PreparePtr prepPtr)
/**************************************************************
* Attribute found - store in mapping (AttributeId, Position)
**************************************************************/
- AttributeHeader & attrMap =
- AttributeHeader::init(attrMappingIt.data,
- attrDesc.AttributeId, // 1. Store AttrId
+ AttributeHeader attrMap(attrDesc.AttributeId, // 1. Store AttrId
0);
if (attrDesc.AttributeKeyFlag) {
@@ -1200,6 +1198,7 @@ DbUtil::prepareOperation(Signal* signal, PreparePtr prepPtr)
return;
}
}
+ *(attrMappingIt.data) = attrMap.m_value;
#if 0
ndbout << "BEFORE: attrLength: " << attrLength << endl;
#endif
diff --git a/storage/ndb/src/kernel/blocks/lgman.cpp b/storage/ndb/src/kernel/blocks/lgman.cpp
index b5e3b55dba5..fc28906f4db 100644
--- a/storage/ndb/src/kernel/blocks/lgman.cpp
+++ b/storage/ndb/src/kernel/blocks/lgman.cpp
@@ -2717,8 +2717,16 @@ Lgman::execute_undo_record(Signal* signal)
Uint32 lcp = * (ptr - len + 1);
if(m_latest_lcp && lcp > m_latest_lcp)
{
- // Just ignore
- break;
+ if (0)
+ {
+ const Uint32 * base = ptr - len + 1;
+ Uint32 lcp = base[0];
+ Uint32 tableId = base[1] >> 16;
+ Uint32 fragId = base[1] & 0xFFFF;
+
+ ndbout_c("NOT! ignoring lcp: %u tab: %u frag: %u",
+ lcp, tableId, fragId);
+ }
}
if(m_latest_lcp == 0 ||
diff --git a/storage/ndb/src/kernel/blocks/pgman.cpp b/storage/ndb/src/kernel/blocks/pgman.cpp
index d8e0c053984..aa1f04c720c 100644
--- a/storage/ndb/src/kernel/blocks/pgman.cpp
+++ b/storage/ndb/src/kernel/blocks/pgman.cpp
@@ -238,6 +238,13 @@ Pgman::execCONTINUEB(Signal* signal)
}
else
{
+ if (ERROR_INSERTED(11007))
+ {
+ ndbout << "No more writes..." << endl;
+ SET_ERROR_INSERT_VALUE(11008);
+ signal->theData[0] = 9999;
+ sendSignalWithDelay(CMVMI_REF, GSN_NDB_TAMPER, signal, 10000, 1);
+ }
signal->theData[0] = m_end_lcp_req.senderData;
sendSignal(m_end_lcp_req.senderRef, GSN_END_LCP_CONF, signal, 1, JBB);
}
@@ -493,6 +500,11 @@ Pgman::release_page_entry(Ptr<Page_entry>& ptr)
if (! (state & Page_entry::LOCKED))
ndbrequire(! (state & Page_entry::REQUEST));
+
+ if (ptr.p->m_copy_page_i != RNIL)
+ {
+ m_global_page_pool.release(ptr.p->m_copy_page_i);
+ }
set_page_state(ptr, 0);
m_page_hashlist.remove(ptr);
@@ -1142,7 +1154,8 @@ Pgman::process_cleanup(Signal* signal)
#ifdef VM_TRACE
debugOut << "PGMAN: " << ptr << " : process_cleanup" << endl;
#endif
- c_tup->disk_page_unmap_callback(ptr.p->m_real_page_i,
+ c_tup->disk_page_unmap_callback(0,
+ ptr.p->m_real_page_i,
ptr.p->m_dirty_count);
pageout(signal, ptr);
max_count--;
@@ -1180,6 +1193,11 @@ Pgman::move_cleanup_ptr(Ptr<Page_entry> ptr)
void
Pgman::execLCP_FRAG_ORD(Signal* signal)
{
+ if (ERROR_INSERTED(11008))
+ {
+ ndbout_c("Ignore LCP_FRAG_ORD");
+ return;
+ }
LcpFragOrd* ord = (LcpFragOrd*)signal->getDataPtr();
ndbrequire(ord->lcpId >= m_last_lcp_complete + 1 || m_last_lcp_complete == 0);
m_last_lcp = ord->lcpId;
@@ -1196,6 +1214,12 @@ Pgman::execLCP_FRAG_ORD(Signal* signal)
void
Pgman::execEND_LCP_REQ(Signal* signal)
{
+ if (ERROR_INSERTED(11008))
+ {
+ ndbout_c("Ignore END_LCP");
+ return;
+ }
+
EndLcpReq* req = (EndLcpReq*)signal->getDataPtr();
m_end_lcp_req = *req;
@@ -1274,7 +1298,8 @@ Pgman::process_lcp(Signal* signal)
{
DBG_LCP(" pageout()" << endl);
ptr.p->m_state |= Page_entry::LCP;
- c_tup->disk_page_unmap_callback(ptr.p->m_real_page_i,
+ c_tup->disk_page_unmap_callback(0,
+ ptr.p->m_real_page_i,
ptr.p->m_dirty_count);
pageout(signal, ptr);
}
@@ -1301,6 +1326,13 @@ Pgman::process_lcp(Signal* signal)
}
else
{
+ if (ERROR_INSERTED(11007))
+ {
+ ndbout << "No more writes..." << endl;
+ signal->theData[0] = 9999;
+ sendSignalWithDelay(CMVMI_REF, GSN_NDB_TAMPER, signal, 10000, 1);
+ SET_ERROR_INSERT_VALUE(11008);
+ }
signal->theData[0] = m_end_lcp_req.senderData;
sendSignal(m_end_lcp_req.senderRef, GSN_END_LCP_CONF, signal, 1, JBB);
}
@@ -1489,6 +1521,10 @@ Pgman::fswriteconf(Signal* signal, Ptr<Page_entry> ptr)
Page_state state = ptr.p->m_state;
ndbrequire(state & Page_entry::PAGEOUT);
+ c_tup->disk_page_unmap_callback(1,
+ ptr.p->m_real_page_i,
+ ptr.p->m_dirty_count);
+
state &= ~ Page_entry::PAGEOUT;
state &= ~ Page_entry::EMPTY;
state &= ~ Page_entry::DIRTY;
@@ -1588,8 +1624,11 @@ Pgman::fswritereq(Signal* signal, Ptr<Page_entry> ptr)
}
#endif
- sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal,
- FsReadWriteReq::FixedLength + 1, JBA);
+ if (!ERROR_INSERTED(11008))
+ {
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal,
+ FsReadWriteReq::FixedLength + 1, JBA);
+ }
}
void
@@ -1739,7 +1778,7 @@ Pgman::get_page(Signal* signal, Ptr<Page_entry> ptr, Page_request page_req)
#endif
state |= Page_entry::REQUEST;
- if (only_request && req_flags & Page_request::EMPTY_PAGE)
+ if (only_request && (req_flags & Page_request::EMPTY_PAGE))
{
state |= Page_entry::EMPTY;
}
@@ -2401,7 +2440,8 @@ Pgman::execDUMP_STATE_ORD(Signal* signal)
if (pl_hash.find(ptr, key))
{
ndbout << "pageout " << ptr << endl;
- c_tup->disk_page_unmap_callback(ptr.p->m_real_page_i,
+ c_tup->disk_page_unmap_callback(0,
+ ptr.p->m_real_page_i,
ptr.p->m_dirty_count);
pageout(signal, ptr);
}
@@ -2452,6 +2492,16 @@ Pgman::execDUMP_STATE_ORD(Signal* signal)
{
SET_ERROR_INSERT_VALUE(11006);
}
+
+ if (signal->theData[0] == 11007)
+ {
+ SET_ERROR_INSERT_VALUE(11007);
+ }
+
+ if (signal->theData[0] == 11008)
+ {
+ SET_ERROR_INSERT_VALUE(11008);
+ }
}
// page cache client
diff --git a/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp b/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp
index 8d51b24ec6a..6a76ce5217a 100644
--- a/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp
+++ b/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp
@@ -265,6 +265,8 @@ private:
void execALLOC_NODEID_CONF(Signal *);
void execALLOC_NODEID_REF(Signal *);
void completeAllocNodeIdReq(Signal *);
+
+ void execSTART_ORD(Signal*);
// Arbitration signals
void execARBIT_CFG(Signal* signal);
@@ -281,6 +283,7 @@ private:
void check_readnodes_reply(Signal* signal, Uint32 nodeId, Uint32 gsn);
Uint32 check_startup(Signal* signal);
+ void api_failed(Signal* signal, Uint32 aFailedNode);
void node_failed(Signal* signal, Uint16 aFailedNode);
void checkStartInterface(Signal* signal);
void failReport(Signal* signal,
diff --git a/storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp b/storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp
index f9950072ab4..2f03bd56694 100644
--- a/storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp
+++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp
@@ -31,10 +31,6 @@ void Qmgr::initData()
cnoCommitFailedNodes = 0;
c_maxDynamicId = 0;
c_clusterNodes.clear();
-
- Uint32 hbDBAPI = 500;
- setHbApiDelay(hbDBAPI);
- c_connectedNodes.set(getOwnNodeId());
c_stopReq.senderRef = 0;
/**
@@ -43,6 +39,27 @@ void Qmgr::initData()
ndbrequire((Uint32)NodeInfo::DB == 0);
ndbrequire((Uint32)NodeInfo::API == 1);
ndbrequire((Uint32)NodeInfo::MGM == 2);
+
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrAss(nodePtr, nodeRec);
+ nodePtr.p->blockRef = reference();
+
+ c_connectedNodes.set(getOwnNodeId());
+ setNodeInfo(getOwnNodeId()).m_version = NDB_VERSION;
+
+
+ /**
+ * Timeouts
+ */
+ const ndb_mgm_configuration_iterator * p =
+ m_ctx.m_config.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ Uint32 hbDBAPI = 1500;
+ ndb_mgm_get_int_parameter(p, CFG_DB_API_HEARTBEAT_INTERVAL, &hbDBAPI);
+
+ setHbApiDelay(hbDBAPI);
}//Qmgr::initData()
void Qmgr::initRecords()
@@ -113,6 +130,7 @@ Qmgr::Qmgr(Block_context& ctx)
addRecSignal(GSN_DIH_RESTARTREF, &Qmgr::execDIH_RESTARTREF);
addRecSignal(GSN_DIH_RESTARTCONF, &Qmgr::execDIH_RESTARTCONF);
addRecSignal(GSN_NODE_VERSION_REP, &Qmgr::execNODE_VERSION_REP);
+ addRecSignal(GSN_START_ORD, &Qmgr::execSTART_ORD);
initData();
}//Qmgr::Qmgr()
diff --git a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp
index 1fba4d62e17..23e7829481e 100644
--- a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp
+++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp
@@ -238,6 +238,38 @@ Qmgr::execREAD_CONFIG_REQ(Signal* signal)
ReadConfigConf::SignalLength, JBB);
}
+void
+Qmgr::execSTART_ORD(Signal* signal)
+{
+ /**
+ * Start timer handling
+ */
+ signal->theData[0] = ZTIMER_HANDLING;
+ sendSignal(QMGR_REF, GSN_CONTINUEB, signal, 1, JBB);
+
+ NodeRecPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NODES; nodePtr.i++)
+ {
+ ptrAss(nodePtr, nodeRec);
+ nodePtr.p->ndynamicId = 0;
+ if(getNodeInfo(nodePtr.i).m_type == NodeInfo::DB)
+ {
+ nodePtr.p->phase = ZINIT;
+ c_definedNodes.set(nodePtr.i);
+ } else {
+ nodePtr.p->phase = ZAPI_INACTIVE;
+ }
+
+ setNodeInfo(nodePtr.i).m_heartbeat_cnt= 0;
+ nodePtr.p->sendPrepFailReqStatus = Q_NOT_ACTIVE;
+ nodePtr.p->sendCommitFailReqStatus = Q_NOT_ACTIVE;
+ nodePtr.p->sendPresToStatus = Q_NOT_ACTIVE;
+ nodePtr.p->failState = NORMAL;
+ nodePtr.p->rcv[0] = 0;
+ nodePtr.p->rcv[1] = 0;
+ }//for
+}
+
/*
4.2 ADD NODE MODULE*/
/*##########################################################################*/
@@ -298,8 +330,6 @@ void Qmgr::startphase1(Signal* signal)
nodePtr.i = getOwnNodeId();
ptrAss(nodePtr, nodeRec);
nodePtr.p->phase = ZSTARTING;
- nodePtr.p->blockRef = reference();
- c_connectedNodes.set(nodePtr.i);
signal->theData[0] = reference();
sendSignal(DBDIH_REF, GSN_DIH_RESTARTREQ, signal, 1, JBB);
@@ -371,11 +401,14 @@ void Qmgr::execCONNECT_REP(Signal* signal)
case ZFAIL_CLOSING:
jam();
return;
- case ZINIT:
- ndbrequire(false);
case ZAPI_ACTIVE:
case ZAPI_INACTIVE:
return;
+ case ZINIT:
+ ndbrequire(getNodeInfo(nodeId).m_type == NodeInfo::MGM);
+ break;
+ default:
+ ndbrequire(false);
}
if (getNodeInfo(nodeId).getType() != NodeInfo::DB)
@@ -1212,12 +1245,6 @@ void Qmgr::execCM_REGREF(Signal* signal)
{
jam();
electionWon(signal);
-
- /**
- * Start timer handling
- */
- signal->theData[0] = ZTIMER_HANDLING;
- sendSignal(QMGR_REF, GSN_CONTINUEB, signal, 10, JBB);
}
return;
@@ -1855,12 +1882,6 @@ Qmgr::joinedCluster(Signal* signal, NodeRecPtr nodePtr){
sendSttorryLab(signal);
- /**
- * Start timer handling
- */
- signal->theData[0] = ZTIMER_HANDLING;
- sendSignal(QMGR_REF, GSN_CONTINUEB, signal, 10, JBB);
-
sendCmAckAdd(signal, getOwnNodeId(), CmAdd::CommitNew);
}
@@ -2094,25 +2115,6 @@ void Qmgr::findNeighbours(Signal* signal)
/*---------------------------------------------------------------------------*/
void Qmgr::initData(Signal* signal)
{
- NodeRecPtr nodePtr;
- for (nodePtr.i = 1; nodePtr.i < MAX_NODES; nodePtr.i++) {
- ptrAss(nodePtr, nodeRec);
- nodePtr.p->ndynamicId = 0;
- if(getNodeInfo(nodePtr.i).m_type == NodeInfo::DB){
- nodePtr.p->phase = ZINIT;
- c_definedNodes.set(nodePtr.i);
- } else {
- nodePtr.p->phase = ZAPI_INACTIVE;
- }
-
- setNodeInfo(nodePtr.i).m_heartbeat_cnt= 0;
- nodePtr.p->sendPrepFailReqStatus = Q_NOT_ACTIVE;
- nodePtr.p->sendCommitFailReqStatus = Q_NOT_ACTIVE;
- nodePtr.p->sendPresToStatus = Q_NOT_ACTIVE;
- nodePtr.p->failState = NORMAL;
- nodePtr.p->rcv[0] = 0;
- nodePtr.p->rcv[1] = 0;
- }//for
cfailureNr = 1;
ccommitFailureNr = 1;
cprepareFailureNr = 1;
@@ -2146,13 +2148,11 @@ void Qmgr::initData(Signal* signal)
ndbrequire(p != 0);
Uint32 hbDBDB = 1500;
- Uint32 hbDBAPI = 1500;
Uint32 arbitTimeout = 1000;
c_restartPartialTimeout = 30000;
c_restartPartionedTimeout = 60000;
c_restartFailureTimeout = ~0;
ndb_mgm_get_int_parameter(p, CFG_DB_HEARTBEAT_INTERVAL, &hbDBDB);
- ndb_mgm_get_int_parameter(p, CFG_DB_API_HEARTBEAT_INTERVAL, &hbDBAPI);
ndb_mgm_get_int_parameter(p, CFG_DB_ARBIT_TIMEOUT, &arbitTimeout);
ndb_mgm_get_int_parameter(p, CFG_DB_START_PARTIAL_TIMEOUT,
&c_restartPartialTimeout);
@@ -2177,7 +2177,6 @@ void Qmgr::initData(Signal* signal)
}
setHbDelay(hbDBDB);
- setHbApiDelay(hbDBAPI);
setArbitTimeout(arbitTimeout);
arbitRec.state = ARBIT_NULL; // start state for all nodes
@@ -2204,7 +2203,6 @@ void Qmgr::initData(Signal* signal)
execARBIT_CFG(signal);
}
- setNodeInfo(getOwnNodeId()).m_version = NDB_VERSION;
}//Qmgr::initData()
@@ -2237,20 +2235,22 @@ void Qmgr::timerHandlingLab(Signal* signal)
hb_check_timer.reset();
}
}
-
+
if (interface_check_timer.check(TcurrentTime)) {
jam();
interface_check_timer.reset();
checkStartInterface(signal);
}
+ if (hb_api_timer.check(TcurrentTime))
+ {
+ jam();
+ hb_api_timer.reset();
+ apiHbHandlingLab(signal);
+ }
+
if (cactivateApiCheck != 0) {
jam();
- if (hb_api_timer.check(TcurrentTime)) {
- jam();
- hb_api_timer.reset();
- apiHbHandlingLab(signal);
- }//if
if (clatestTransactionCheck == 0) {
//-------------------------------------------------------------
// Initialise the Transaction check timer.
@@ -2367,18 +2367,21 @@ void Qmgr::apiHbHandlingLab(Signal* signal)
if(type == NodeInfo::INVALID)
continue;
- if (TnodePtr.p->phase == ZAPI_ACTIVE){
+ if (c_connectedNodes.get(nodeId))
+ {
jam();
setNodeInfo(TnodePtr.i).m_heartbeat_cnt++;
- if(getNodeInfo(TnodePtr.i).m_heartbeat_cnt > 2){
+ if(getNodeInfo(TnodePtr.i).m_heartbeat_cnt > 2)
+ {
signal->theData[0] = NDB_LE_MissedHeartbeat;
signal->theData[1] = nodeId;
signal->theData[2] = getNodeInfo(TnodePtr.i).m_heartbeat_cnt - 1;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
}
- if (getNodeInfo(TnodePtr.i).m_heartbeat_cnt > 4) {
+ if (getNodeInfo(TnodePtr.i).m_heartbeat_cnt > 4)
+ {
jam();
/*------------------------------------------------------------------*/
/* THE API NODE HAS NOT SENT ANY HEARTBEAT FOR THREE SECONDS.
@@ -2390,8 +2393,8 @@ void Qmgr::apiHbHandlingLab(Signal* signal)
signal->theData[0] = NDB_LE_DeadDueToHeartbeat;
signal->theData[1] = nodeId;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
-
- node_failed(signal, nodeId);
+
+ api_failed(signal, nodeId);
}//if
}//if
}//for
@@ -2480,26 +2483,6 @@ void Qmgr::sendApiFailReq(Signal* signal, Uint16 failedNodeNo)
sendSignal(DBTC_REF, GSN_API_FAILREQ, signal, 2, JBA);
sendSignal(DBDICT_REF, GSN_API_FAILREQ, signal, 2, JBA);
sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBA);
-
- /**-------------------------------------------------------------------------
- * THE OTHER NODE WAS AN API NODE. THE COMMUNICATION LINK IS ALREADY
- * BROKEN AND THUS NO ACTION IS NEEDED TO BREAK THE CONNECTION.
- * WE ONLY NEED TO SET PARAMETERS TO ENABLE A NEW CONNECTION IN A FEW
- * SECONDS.
- *-------------------------------------------------------------------------*/
- setNodeInfo(failedNodePtr.i).m_heartbeat_cnt= 0;
- setNodeInfo(failedNodePtr.i).m_version = 0;
- recompute_version_info(getNodeInfo(failedNodePtr.i).m_type);
-
- CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
-
- closeCom->xxxBlockRef = reference();
- closeCom->failNo = 0;
- closeCom->noOfNodes = 1;
- NodeBitmask::clear(closeCom->theNodes);
- NodeBitmask::set(closeCom->theNodes, failedNodePtr.i);
- sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal,
- CloseComReqConf::SignalLength, JBA);
}//Qmgr::sendApiFailReq()
void Qmgr::execAPI_FAILREQ(Signal* signal)
@@ -2512,20 +2495,7 @@ void Qmgr::execAPI_FAILREQ(Signal* signal)
ndbrequire(getNodeInfo(failedNodePtr.i).getType() != NodeInfo::DB);
- // ignore if api not active
- if (failedNodePtr.p->phase != ZAPI_ACTIVE)
- {
- jam();
- // But send to SUMA anyway...
- sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBA);
- return;
- }
-
- signal->theData[0] = NDB_LE_Disconnected;
- signal->theData[1] = failedNodePtr.i;
- sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
-
- node_failed(signal, failedNodePtr.i);
+ api_failed(signal, signal->theData[0]);
}
void Qmgr::execAPI_FAILCONF(Signal* signal)
@@ -2649,6 +2619,13 @@ void Qmgr::execDISCONNECT_REP(Signal* signal)
ndbrequire(false);
}
+ if (getNodeInfo(nodeId).getType() != NodeInfo::DB)
+ {
+ jam();
+ api_failed(signal, nodeId);
+ return;
+ }
+
switch(nodePtr.p->phase){
case ZRUNNING:
jam();
@@ -2685,66 +2662,109 @@ void Qmgr::node_failed(Signal* signal, Uint16 aFailedNode)
failedNodePtr.i = aFailedNode;
ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
- if (getNodeInfo(failedNodePtr.i).getType() == NodeInfo::DB){
+ ndbrequire(getNodeInfo(failedNodePtr.i).getType() == NodeInfo::DB);
+
+ /**---------------------------------------------------------------------
+ * THE OTHER NODE IS AN NDB NODE, WE HANDLE IT AS IF A HEARTBEAT
+ * FAILURE WAS DISCOVERED.
+ *---------------------------------------------------------------------*/
+ switch(failedNodePtr.p->phase){
+ case ZRUNNING:
jam();
- /**---------------------------------------------------------------------
- * THE OTHER NODE IS AN NDB NODE, WE HANDLE IT AS IF A HEARTBEAT
- * FAILURE WAS DISCOVERED.
- *---------------------------------------------------------------------*/
- switch(failedNodePtr.p->phase){
- case ZRUNNING:
- jam();
- failReportLab(signal, aFailedNode, FailRep::ZLINK_FAILURE);
- return;
- case ZFAIL_CLOSING:
- jam();
- return;
- case ZSTARTING:
- c_start.reset();
- // Fall-through
- default:
- jam();
- /*---------------------------------------------------------------------*/
- // The other node is still not in the cluster but disconnected.
- // We must restart communication in three seconds.
- /*---------------------------------------------------------------------*/
- failedNodePtr.p->failState = NORMAL;
- failedNodePtr.p->phase = ZFAIL_CLOSING;
- setNodeInfo(failedNodePtr.i).m_heartbeat_cnt= 0;
-
- CloseComReqConf * const closeCom =
- (CloseComReqConf *)&signal->theData[0];
-
- closeCom->xxxBlockRef = reference();
- closeCom->failNo = 0;
- closeCom->noOfNodes = 1;
- NodeBitmask::clear(closeCom->theNodes);
- NodeBitmask::set(closeCom->theNodes, failedNodePtr.i);
- sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal,
- CloseComReqConf::SignalLength, JBA);
- }//if
+ failReportLab(signal, aFailedNode, FailRep::ZLINK_FAILURE);
return;
- }
-
- /**
- * API code
- */
- jam();
- if (failedNodePtr.p->phase != ZFAIL_CLOSING){
+ case ZFAIL_CLOSING:
+ jam();
+ return;
+ case ZSTARTING:
+ c_start.reset();
+ // Fall-through
+ default:
jam();
- //-------------------------------------------------------------------------
- // The API was active and has now failed. We need to initiate API failure
- // handling. If the API had already failed then we can ignore this
- // discovery.
- //-------------------------------------------------------------------------
+ /*---------------------------------------------------------------------*/
+ // The other node is still not in the cluster but disconnected.
+ // We must restart communication in three seconds.
+ /*---------------------------------------------------------------------*/
+ failedNodePtr.p->failState = NORMAL;
failedNodePtr.p->phase = ZFAIL_CLOSING;
-
- sendApiFailReq(signal, aFailedNode);
- arbitRec.code = ArbitCode::ApiFail;
- handleArbitApiFail(signal, aFailedNode);
+ setNodeInfo(failedNodePtr.i).m_heartbeat_cnt= 0;
+
+ CloseComReqConf * const closeCom =
+ (CloseComReqConf *)&signal->theData[0];
+
+ closeCom->xxxBlockRef = reference();
+ closeCom->failNo = 0;
+ closeCom->noOfNodes = 1;
+ NodeBitmask::clear(closeCom->theNodes);
+ NodeBitmask::set(closeCom->theNodes, failedNodePtr.i);
+ sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal,
+ CloseComReqConf::SignalLength, JBA);
}//if
return;
-}//Qmgr::node_failed()
+}
+
+void
+Qmgr::api_failed(Signal* signal, Uint32 nodeId)
+{
+ NodeRecPtr failedNodePtr;
+ /**------------------------------------------------------------------------
+ * A COMMUNICATION LINK HAS BEEN DISCONNECTED. WE MUST TAKE SOME ACTION
+ * DUE TO THIS.
+ *-----------------------------------------------------------------------*/
+ failedNodePtr.i = nodeId;
+ ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
+
+ if (failedNodePtr.p->phase == ZFAIL_CLOSING)
+ {
+ /**
+ * Failure handling already in progress
+ */
+ jam();
+ return;
+ }
+
+ if (failedNodePtr.p->phase == ZAPI_ACTIVE)
+ {
+ jam();
+ sendApiFailReq(signal, nodeId);
+ arbitRec.code = ArbitCode::ApiFail;
+ handleArbitApiFail(signal, nodeId);
+ }
+ else
+ {
+ /**
+ * Always inform SUMA
+ */
+ jam();
+ signal->theData[0] = nodeId;
+ signal->theData[1] = QMGR_REF;
+ sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBA);
+ failedNodePtr.p->failState = NORMAL;
+ }
+
+ failedNodePtr.p->phase = ZFAIL_CLOSING;
+ setNodeInfo(failedNodePtr.i).m_heartbeat_cnt= 0;
+ setNodeInfo(failedNodePtr.i).m_version = 0;
+ recompute_version_info(getNodeInfo(failedNodePtr.i).m_type);
+
+ CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
+ closeCom->xxxBlockRef = reference();
+ closeCom->failNo = 0;
+ closeCom->noOfNodes = 1;
+ NodeBitmask::clear(closeCom->theNodes);
+ NodeBitmask::set(closeCom->theNodes, failedNodePtr.i);
+ sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal,
+ CloseComReqConf::SignalLength, JBA);
+
+ if (getNodeInfo(failedNodePtr.i).getType() == NodeInfo::MGM)
+ {
+ /**
+ * Allow MGM do reconnect "directly"
+ */
+ jam();
+ setNodeInfo(failedNodePtr.i).m_heartbeat_cnt = 3;
+ }
+}
/**--------------------------------------------------------------------------
* AN API NODE IS REGISTERING. IF FOR THE FIRST TIME WE WILL ENABLE
@@ -4963,43 +4983,39 @@ Qmgr::execDUMP_STATE_ORD(Signal* signal)
c_start.m_president_candidate_gci);
infoEvent("ctoStatus = %d\n", ctoStatus);
for(Uint32 i = 1; i<MAX_NDB_NODES; i++){
- if(getNodeInfo(i).getType() == NodeInfo::DB){
- NodeRecPtr nodePtr;
- nodePtr.i = i;
- ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
- char buf[100];
- switch(nodePtr.p->phase){
- case ZINIT:
- sprintf(buf, "Node %d: ZINIT(%d)", i, nodePtr.p->phase);
- break;
- case ZSTARTING:
- sprintf(buf, "Node %d: ZSTARTING(%d)", i, nodePtr.p->phase);
- break;
- case ZRUNNING:
- sprintf(buf, "Node %d: ZRUNNING(%d)", i, nodePtr.p->phase);
- break;
- case ZPREPARE_FAIL:
- sprintf(buf, "Node %d: ZPREPARE_FAIL(%d)", i, nodePtr.p->phase);
- break;
- case ZFAIL_CLOSING:
- sprintf(buf, "Node %d: ZFAIL_CLOSING(%d)", i, nodePtr.p->phase);
- break;
- case ZAPI_INACTIVE:
- sprintf(buf, "Node %d: ZAPI_INACTIVE(%d)", i, nodePtr.p->phase);
- break;
- case ZAPI_ACTIVE:
- sprintf(buf, "Node %d: ZAPI_ACTIVE(%d)", i, nodePtr.p->phase);
- break;
- default:
- sprintf(buf, "Node %d: <UNKNOWN>(%d)", i, nodePtr.p->phase);
- break;
- }
- infoEvent(buf);
+ NodeRecPtr nodePtr;
+ nodePtr.i = i;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
+ char buf[100];
+ switch(nodePtr.p->phase){
+ case ZINIT:
+ sprintf(buf, "Node %d: ZINIT(%d)", i, nodePtr.p->phase);
+ break;
+ case ZSTARTING:
+ sprintf(buf, "Node %d: ZSTARTING(%d)", i, nodePtr.p->phase);
+ break;
+ case ZRUNNING:
+ sprintf(buf, "Node %d: ZRUNNING(%d)", i, nodePtr.p->phase);
+ break;
+ case ZPREPARE_FAIL:
+ sprintf(buf, "Node %d: ZPREPARE_FAIL(%d)", i, nodePtr.p->phase);
+ break;
+ case ZFAIL_CLOSING:
+ sprintf(buf, "Node %d: ZFAIL_CLOSING(%d)", i, nodePtr.p->phase);
+ break;
+ case ZAPI_INACTIVE:
+ sprintf(buf, "Node %d: ZAPI_INACTIVE(%d)", i, nodePtr.p->phase);
+ break;
+ case ZAPI_ACTIVE:
+ sprintf(buf, "Node %d: ZAPI_ACTIVE(%d)", i, nodePtr.p->phase);
+ break;
+ default:
+ sprintf(buf, "Node %d: <UNKNOWN>(%d)", i, nodePtr.p->phase);
+ break;
}
+ infoEvent(buf);
}
- default:
- ;
- }//switch
+ }
#ifdef ERROR_INSERT
if (signal->theData[0] == 935 && signal->getLength() == 2)
diff --git a/storage/ndb/src/kernel/blocks/suma/Suma.cpp b/storage/ndb/src/kernel/blocks/suma/Suma.cpp
index b8cc438f514..113b63a19d3 100644
--- a/storage/ndb/src/kernel/blocks/suma/Suma.cpp
+++ b/storage/ndb/src/kernel/blocks/suma/Suma.cpp
@@ -821,8 +821,9 @@ Suma::execINCL_NODEREQ(Signal* signal){
ndbrequire(!c_alive_nodes.get(nodeId));
c_alive_nodes.set(nodeId);
- signal->theData[0] = reference();
- sendSignal(senderRef, GSN_INCL_NODECONF, signal, 1, JBB);
+ signal->theData[0] = nodeId;
+ signal->theData[1] = reference();
+ sendSignal(senderRef, GSN_INCL_NODECONF, signal, 2, JBB);
}
void
@@ -974,6 +975,54 @@ Suma::execDUMP_STATE_ORD(Signal* signal){
}
return;
}
+
+ if (tCase == 8011)
+ {
+ jam();
+ Uint32 bucket = signal->theData[1];
+ KeyTable<Table>::Iterator it;
+ if (signal->getLength() == 1)
+ {
+ jam();
+ bucket = 0;
+ infoEvent("-- Starting dump of subscribers --");
+ }
+
+ c_tables.next(bucket, it);
+ const Uint32 RT_BREAK = 16;
+ for(Uint32 i = 0; i<RT_BREAK || it.bucket == bucket; i++)
+ {
+ jam();
+ if(it.curr.i == RNIL)
+ {
+ jam();
+ infoEvent("-- Ending dump of subscribers --");
+ return;
+ }
+
+ infoEvent("Table: %u ver: %u #n: %u (ref,data,subscritopn)",
+ it.curr.p->m_tableId,
+ it.curr.p->m_schemaVersion,
+ it.curr.p->n_subscribers);
+
+ Ptr<Subscriber> ptr;
+ LocalDLList<Subscriber> list(c_subscriberPool, it.curr.p->c_subscribers);
+ for (list.first(ptr); !ptr.isNull(); list.next(ptr), i++)
+ {
+ jam();
+ infoEvent(" [ %x %u %u ]",
+ ptr.p->m_senderRef,
+ ptr.p->m_senderData,
+ ptr.p->m_subPtrI);
+ }
+ c_tables.next(it);
+ }
+
+ signal->theData[0] = tCase;
+ signal->theData[1] = it.bucket;
+ sendSignalWithDelay(reference(), GSN_DUMP_STATE_ORD, signal, 100, 2);
+ return;
+ }
}
/*************************************************************
@@ -2402,6 +2451,7 @@ Suma::execSUB_START_REQ(Signal* signal){
{
jam();
+ c_subscriberPool.release(subbPtr);
sendSubStartRef(signal, SubStartRef::PartiallyConnected);
return;
}
@@ -3655,6 +3705,8 @@ Suma::execSUB_GCP_COMPLETE_REP(Signal* signal)
if(m_gcp_complete_rep_count && !c_subscriber_nodes.isclear())
{
+ CRASH_INSERTION(13033);
+
NodeReceiverGroup rg(API_CLUSTERMGR, c_subscriber_nodes);
sendSignal(rg, GSN_SUB_GCP_COMPLETE_REP, signal,
SubGcpCompleteRep::SignalLength, JBB);
@@ -3674,8 +3726,8 @@ Suma::execSUB_GCP_COMPLETE_REP(Signal* signal)
{
if(m_active_buckets.get(i))
continue;
-
- if(c_buckets[i].m_buffer_tail != RNIL)
+
+ if (!c_subscriber_nodes.isclear())
{
//Uint32* dst;
get_buffer_ptr(signal, i, gci, 0);
diff --git a/storage/ndb/src/kernel/blocks/tsman.cpp b/storage/ndb/src/kernel/blocks/tsman.cpp
index 62d169a0081..8e68e118f98 100644
--- a/storage/ndb/src/kernel/blocks/tsman.cpp
+++ b/storage/ndb/src/kernel/blocks/tsman.cpp
@@ -299,7 +299,7 @@ Tsman::execDUMP_STATE_ORD(Signal* signal){
Uint32 new_bits = curr_bits ^ rand();
Local_key key = chunks[chunk].start_page;
key.m_page_no += page;
- ndbrequire(update_page_free_bits(signal, &key, new_bits, 0) == 0);
+ ndbrequire(update_page_free_bits(signal, &key, new_bits) == 0);
}
}
}
@@ -366,6 +366,20 @@ Tsman::execCREATE_FILEGROUP_REQ(Signal* signal){
CreateFilegroupImplRef::SignalLength, JBB);
}
+NdbOut&
+operator<<(NdbOut& out, const File_formats::Datafile::Extent_header & obj)
+{
+ out << "table: " << obj.m_table
+ << " fragment: " << obj.m_fragment_id << " ";
+ for(Uint32 i = 0; i<32; i++)
+ {
+ char t[2];
+ BaseString::snprintf(t, sizeof(t), "%x", obj.get_free_bits(i));
+ out << t;
+ }
+ return out;
+}
+
void
Tsman::execDROP_FILEGROUP_REQ(Signal* signal){
jamEntry();
@@ -1606,8 +1620,7 @@ Tsman::execFREE_EXTENT_REQ(Signal* signal)
int
Tsman::update_page_free_bits(Signal* signal,
Local_key *key,
- unsigned committed_bits,
- Uint64 lsn)
+ unsigned committed_bits)
{
jamEntry();
@@ -1642,6 +1655,18 @@ Tsman::update_page_free_bits(Signal* signal,
File_formats::Datafile::Extent_header* header =
page->get_header(val.m_extent_no, val.m_extent_size);
+ if (header->m_table == RNIL)
+ {
+ ndbout << "update page free bits page: " << *key
+ << " " << *header << endl;
+ }
+
+ if (0)
+ {
+ ndbout << "update page free bits page(" << committed_bits << ") "
+ << *key << " " << *header << endl;
+ }
+
ndbrequire(header->m_table != RNIL);
Uint32 page_no_in_extent = calc_page_no_in_extent(key->m_page_no, &val);
@@ -1653,7 +1678,7 @@ Tsman::update_page_free_bits(Signal* signal,
Uint32 src = header->get_free_bits(page_no_in_extent) & UNCOMMITTED_MASK;
header->update_free_bits(page_no_in_extent, src | committed_bits);
- m_page_cache_client.update_lsn(preq.m_page, lsn);
+ m_page_cache_client.update_lsn(preq.m_page, 0);
return 0;
}
@@ -1741,6 +1766,11 @@ Tsman::unmap_page(Signal* signal, Local_key *key, Uint32 uncommitted_bits)
File_formats::Datafile::Extent_header* header =
page->get_header(val.m_extent_no, val.m_extent_size);
+ if (header->m_table == RNIL)
+ {
+ ndbout << "trying to unmap page: " << *key
+ << " " << *header << endl;
+ }
ndbrequire(header->m_table != RNIL);
Uint32 page_no_in_extent = calc_page_no_in_extent(key->m_page_no, &val);
@@ -1762,9 +1792,7 @@ Tsman::restart_undo_page_free_bits(Signal* signal,
Uint32 tableId,
Uint32 fragId,
Local_key *key,
- unsigned bits,
- Uint64 undo_lsn,
- Uint64 page_lsn)
+ unsigned bits)
{
jamEntry();
@@ -1798,21 +1826,7 @@ Tsman::restart_undo_page_free_bits(Signal* signal,
(File_formats::Datafile::Extent_page*)ptr_p;
File_formats::Datafile::Extent_header* header =
page->get_header(val.m_extent_no, val.m_extent_size);
-
- Uint64 lsn = 0;
- lsn += page->m_page_header.m_page_lsn_hi; lsn <<= 32;
- lsn += page->m_page_header.m_page_lsn_lo;
-
- if (undo_lsn > lsn && undo_lsn > page_lsn)
- {
- if (DBG_UNDO)
- ndbout << "tsman: ignore " << undo_lsn << "(" << lsn << ", "
- << page_lsn << ") "
- << *key << " "
- << " -> " << bits << endl;
- return 0;
- }
-
+
if (header->m_table == RNIL)
{
if (DBG_UNDO)
@@ -1831,7 +1845,7 @@ Tsman::restart_undo_page_free_bits(Signal* signal,
*/
if (DBG_UNDO)
{
- ndbout << "tsman: apply " << undo_lsn << "(" << lsn << ") "
+ ndbout << "tsman: apply "
<< *key << " " << (src & COMMITTED_MASK)
<< " -> " << bits << endl;
}
@@ -1879,7 +1893,7 @@ Tsman::execALLOC_PAGE_REQ(Signal* signal)
/**
* Handling of unmapped extent header pages is not implemented
*/
- int flags = 0;
+ int flags = Page_cache_client::DIRTY_REQ;
int real_page_id;
Uint32 page_no;
Uint32 src_bits;
diff --git a/storage/ndb/src/kernel/blocks/tsman.hpp b/storage/ndb/src/kernel/blocks/tsman.hpp
index 1293cc54141..20019e6d4d1 100644
--- a/storage/ndb/src/kernel/blocks/tsman.hpp
+++ b/storage/ndb/src/kernel/blocks/tsman.hpp
@@ -209,12 +209,12 @@ private:
void load_extent_page_callback(Signal*, Uint32, Uint32);
void create_file_ref(Signal*, Ptr<Tablespace>, Ptr<Datafile>,
Uint32,Uint32,Uint32);
- int update_page_free_bits(Signal*, Local_key*, unsigned committed_bits,
- Uint64 lsn);
+ int update_page_free_bits(Signal*, Local_key*, unsigned committed_bits);
+
int get_page_free_bits(Signal*, Local_key*, unsigned*, unsigned*);
int unmap_page(Signal*, Local_key*, unsigned uncommitted_bits);
int restart_undo_page_free_bits(Signal*, Uint32, Uint32, Local_key*,
- unsigned committed_bits, Uint64, Uint64);
+ unsigned committed_bits);
int alloc_extent(Signal* signal, Uint32 tablespace, Local_key* key);
int alloc_page_from_extent(Signal*, Uint32, Local_key*, Uint32 bits);
@@ -320,7 +320,7 @@ public:
/**
* Update page free bits
*/
- int update_page_free_bits(Local_key*, unsigned bits, Uint64 lsn);
+ int update_page_free_bits(Local_key*, unsigned bits);
/**
* Get page free bits
@@ -336,8 +336,7 @@ public:
/**
* Undo handling of page bits
*/
- int restart_undo_page_free_bits(Local_key*, unsigned bits,
- Uint64 lsn, Uint64 page_lsn);
+ int restart_undo_page_free_bits(Local_key*, unsigned bits);
/**
* Get tablespace info
@@ -417,10 +416,9 @@ Tablespace_client::free_extent(Local_key* key, Uint64 lsn)
inline
int
Tablespace_client::update_page_free_bits(Local_key *key,
- unsigned committed_bits,
- Uint64 lsn)
+ unsigned committed_bits)
{
- return m_tsman->update_page_free_bits(m_signal, key, committed_bits, lsn);
+ return m_tsman->update_page_free_bits(m_signal, key, committed_bits);
}
inline
@@ -442,17 +440,13 @@ Tablespace_client::unmap_page(Local_key *key, unsigned uncommitted_bits)
inline
int
Tablespace_client::restart_undo_page_free_bits(Local_key* key,
- unsigned committed_bits,
- Uint64 lsn,
- Uint64 page_lsn)
+ unsigned committed_bits)
{
return m_tsman->restart_undo_page_free_bits(m_signal,
m_table_id,
m_fragment_id,
key,
- committed_bits,
- lsn,
- page_lsn);
+ committed_bits);
}
#endif
diff --git a/storage/ndb/src/kernel/vm/SimulatedBlock.cpp b/storage/ndb/src/kernel/vm/SimulatedBlock.cpp
index bc16b9f364e..2963b8a02ad 100644
--- a/storage/ndb/src/kernel/vm/SimulatedBlock.cpp
+++ b/storage/ndb/src/kernel/vm/SimulatedBlock.cpp
@@ -1056,6 +1056,7 @@ SimulatedBlock::assembleFragments(Signal * signal){
/**
* Don't release allocated segments
*/
+ signal->header.m_fragmentInfo = 0;
signal->header.m_noOfSections = 0;
return false;
}
@@ -1083,6 +1084,7 @@ SimulatedBlock::assembleFragments(Signal * signal){
* fragInfo = 2
*/
if(fragInfo == 2){
+ signal->header.m_fragmentInfo = 0;
signal->header.m_noOfSections = 0;
return false;
}
diff --git a/storage/ndb/src/mgmclient/CommandInterpreter.cpp b/storage/ndb/src/mgmclient/CommandInterpreter.cpp
index 875cc2771ae..7057525efc7 100644
--- a/storage/ndb/src/mgmclient/CommandInterpreter.cpp
+++ b/storage/ndb/src/mgmclient/CommandInterpreter.cpp
@@ -269,8 +269,8 @@ static const char* helpText =
"CLUSTERLOG TOGGLE [<severity>] ... Toggle severity filter on/off\n"
"CLUSTERLOG INFO Print cluster log information\n"
"<id> START Start data node (started with -n)\n"
-"<id> RESTART [-n] [-i] Restart data or management server node\n"
-"<id> STOP Stop data or management server node\n"
+"<id> RESTART [-n] [-i] [-a] Restart data or management server node\n"
+"<id> STOP [-a] Stop data or management server node\n"
"ENTER SINGLE USER MODE <id> Enter single user mode\n"
"EXIT SINGLE USER MODE Exit single user mode\n"
"<id> STATUS Print status\n"
@@ -434,7 +434,7 @@ static const char* helpTextRestart =
" NDB Cluster -- Management Client -- Help for RESTART command\n"
"---------------------------------------------------------------------------\n"
"RESTART Restart data or management server node\n\n"
-"<id> RESTART [-n] [-i] \n"
+"<id> RESTART [-n] [-i] [-a]\n"
" Restart the data or management node <id>(or All data nodes).\n\n"
" -n (--nostart) restarts the node but does not\n"
" make it join the cluster. Use '<id> START' to\n"
@@ -445,6 +445,7 @@ static const char* helpTextRestart =
" in the same node group during start up.\n\n"
" Consult the documentation before using -i.\n\n"
" INCORRECT USE OF -i WILL CAUSE DATA LOSS!\n"
+" -a Aborts the node, not syncing GCP.\n"
;
static const char* helpTextStop =
@@ -452,10 +453,11 @@ static const char* helpTextStop =
" NDB Cluster -- Management Client -- Help for STOP command\n"
"---------------------------------------------------------------------------\n"
"STOP Stop data or management server node\n\n"
-"<id> STOP Stop the data or management server node <id>.\n\n"
+"<id> STOP [-a] Stop the data or management server node <id>.\n\n"
" ALL STOP will just stop all data nodes.\n\n"
" If you desire to also shut down management servers,\n"
-" use SHUTDOWN instead.\n"
+" use SHUTDOWN instead.\n"
+" -a Aborts the node, not syncing GCP.\n"
;
static const char* helpTextEnterSingleUserMode =
@@ -857,10 +859,14 @@ event_thread_run(void* p)
{
do_event_thread= 1;
do {
- if (ndb_logevent_get_next(log_handle, &log_event, 2000) <= 0)
- continue;
- Guard g(printmutex);
- printLogEvent(&log_event);
+ int res= ndb_logevent_get_next(log_handle, &log_event, 2000);
+ if (res > 0)
+ {
+ Guard g(printmutex);
+ printLogEvent(&log_event);
+ }
+ else if (res < 0)
+ break;
} while(do_event_thread);
ndb_mgm_destroy_logevent_handle(&log_handle);
}
@@ -2661,8 +2667,9 @@ CommandInterpreter::executeStartBackup(char* parameters, bool interactive)
{
int count = 0;
int retry = 0;
+ int res;
do {
- if (ndb_logevent_get_next(log_handle, &log_event, 60000) > 0)
+ if ((res= ndb_logevent_get_next(log_handle, &log_event, 60000)) > 0)
{
int print = 0;
switch (log_event.type) {
@@ -2692,7 +2699,7 @@ CommandInterpreter::executeStartBackup(char* parameters, bool interactive)
{
retry++;
}
- } while(count < 2 && retry < 3);
+ } while(res >= 0 && count < 2 && retry < 3);
if (retry >= 3)
ndbout << "get backup event failed for " << retry << " times" << endl;
diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
index af708664a69..184d51bad78 100644
--- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
+++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
@@ -119,7 +119,11 @@ operator<<(NdbOut& out, const LogLevel & ll)
void
MgmtSrvr::logLevelThreadRun()
{
- while (!_isStopThread) {
+ while (!_isStopThread)
+ {
+ Vector<NodeId> failed_started_nodes;
+ Vector<EventSubscribeReq> failed_log_level_requests;
+
/**
* Handle started nodes
*/
@@ -144,14 +148,15 @@ MgmtSrvr::logLevelThreadRun()
m_started_nodes.unlock();
if (setEventReportingLevelImpl(node, req))
- {
- ndbout_c("setEventReportingLevelImpl(%d): failed", node);
- }
-
- SetLogLevelOrd ord;
- ord = m_nodeLogLevel[node];
- setNodeLogLevelImpl(node, ord);
-
+ {
+ failed_started_nodes.push_back(node);
+ }
+ else
+ {
+ SetLogLevelOrd ord;
+ ord = m_nodeLogLevel[node];
+ setNodeLogLevelImpl(node, ord);
+ }
m_started_nodes.lock();
}
}
@@ -166,17 +171,20 @@ MgmtSrvr::logLevelThreadRun()
if(req.blockRef == 0)
{
- req.blockRef = _ownReference;
- if (setEventReportingLevelImpl(0, req))
- {
- ndbout_c("setEventReportingLevelImpl: failed 2!");
- }
+ req.blockRef = _ownReference;
+ if (setEventReportingLevelImpl(0, req))
+ {
+ failed_log_level_requests.push_back(req);
+ }
}
else
{
SetLogLevelOrd ord;
ord = req;
- setNodeLogLevelImpl(req.blockRef, ord);
+ if (setNodeLogLevelImpl(req.blockRef, ord))
+ {
+ failed_log_level_requests.push_back(req);
+ }
}
m_log_level_requests.lock();
}
@@ -185,7 +193,28 @@ MgmtSrvr::logLevelThreadRun()
if(!ERROR_INSERTED(10000))
m_event_listner.check_listeners();
- NdbSleep_MilliSleep(_logLevelThreadSleep);
+ Uint32 sleeptime = _logLevelThreadSleep;
+ if (failed_started_nodes.size())
+ {
+ m_started_nodes.lock();
+ for (Uint32 i = 0; i<failed_started_nodes.size(); i++)
+ m_started_nodes.push_back(failed_started_nodes[i], false);
+ m_started_nodes.unlock();
+ failed_started_nodes.clear();
+ sleeptime = 100;
+ }
+
+ if (failed_log_level_requests.size())
+ {
+ m_log_level_requests.lock();
+ for (Uint32 i = 0; i<failed_log_level_requests.size(); i++)
+ m_log_level_requests.push_back(failed_log_level_requests[i], false);
+ m_log_level_requests.unlock();
+ failed_log_level_requests.clear();
+ sleeptime = 100;
+ }
+
+ NdbSleep_MilliSleep(sleeptime);
}
}
@@ -234,10 +263,10 @@ MgmtSrvr::startEventLog()
}
}
-void
-MgmtSrvr::stopEventLog()
+void
+MgmtSrvr::stopEventLog()
{
- // Nothing yet
+ g_eventLogger.close();
}
bool
@@ -1535,7 +1564,6 @@ MgmtSrvr::setEventReportingLevelImpl(int nodeId_arg,
{
SignalSender ss(theFacade);
NdbNodeBitmask nodes;
- int retries = 30;
nodes.clear();
while (1)
{
@@ -1572,18 +1600,8 @@ MgmtSrvr::setEventReportingLevelImpl(int nodeId_arg,
continue;
}
// api_reg_conf not recevied yet, need to retry
- break;
- }
- }
- if (nodeId <= max)
- {
- if (--retries)
- {
- ss.unlock();
- NdbSleep_MilliSleep(100);
- continue;
+ return SEND_OR_RECEIVE_FAILED;
}
- return SEND_OR_RECEIVE_FAILED;
}
if (nodeId_arg == 0)
@@ -1607,6 +1625,10 @@ MgmtSrvr::setEventReportingLevelImpl(int nodeId_arg,
continue; // node is not connected, skip
if (ss.sendSignal(nodeId, &ssig) == SEND_OK)
nodes.set(nodeId);
+ else if (max == nodeId)
+ {
+ return SEND_OR_RECEIVE_FAILED;
+ }
}
break;
}
@@ -2988,8 +3010,7 @@ int MgmtSrvr::connect_to_self(void)
return 0;
}
-
-
template class MutexVector<unsigned short>;
template class MutexVector<Ndb_mgmd_event_service::Event_listener>;
+template class Vector<EventSubscribeReq>;
template class MutexVector<EventSubscribeReq>;
diff --git a/storage/ndb/src/mgmsrv/Services.cpp b/storage/ndb/src/mgmsrv/Services.cpp
index 9272b5ab532..eeae61025b1 100644
--- a/storage/ndb/src/mgmsrv/Services.cpp
+++ b/storage/ndb/src/mgmsrv/Services.cpp
@@ -968,7 +968,6 @@ printNodeStatus(OutputStream *output,
MgmtSrvr &mgmsrv,
enum ndb_mgm_node_type type) {
NodeId nodeId = 0;
- mgmsrv.updateStatus();
while(mgmsrv.getNextNodeId(&nodeId, type)) {
enum ndb_mgm_node_status status;
Uint32 startPhase = 0,
@@ -1018,6 +1017,7 @@ MgmApiSession::getStatus(Parser<MgmApiSession>::Context &,
m_output->println("node status");
SLEEP_ERROR_INSERTED(6);
m_output->println("nodes: %d", noOfNodes);
+ m_mgmsrv.updateStatus();
SLEEP_ERROR_INSERTED(7);
printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_NDB);
printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_MGM);
diff --git a/storage/ndb/src/ndbapi/ClusterMgr.cpp b/storage/ndb/src/ndbapi/ClusterMgr.cpp
index 448bc1025e8..f5cc44f70d7 100644
--- a/storage/ndb/src/ndbapi/ClusterMgr.cpp
+++ b/storage/ndb/src/ndbapi/ClusterMgr.cpp
@@ -204,7 +204,10 @@ ClusterMgr::forceHB()
theFacade.sendSignalUnCond(&signal, nodeId);
}
- NdbCondition_WaitTimeout(waitForHBCond, theFacade.theMutexPtr, 1000);
+ /* Wait for nodes to reply - if any heartbeats was sent */
+ if (!waitForHBFromNodes.isclear())
+ NdbCondition_WaitTimeout(waitForHBCond, theFacade.theMutexPtr, 1000);
+
waitingForHB= false;
#ifdef DEBUG_REG
ndbout << "Still waiting for HB from " << waitForHBFromNodes.getText(buf) << endl;
diff --git a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp
index 0fc96add084..f3d1dbe3dd1 100644
--- a/storage/ndb/src/ndbapi/NdbBlob.cpp
+++ b/storage/ndb/src/ndbapi/NdbBlob.cpp
@@ -1141,7 +1141,7 @@ NdbBlob::readTableParts(char* buf, Uint32 part, Uint32 count)
* table tuple does not fully protect blob parts since DBTUP
* commits each tuple separately.
*/
- tOp->readTuple() == -1 ||
+ tOp->readTuple(NdbOperation::LM_SimpleRead) == -1 ||
setPartKeyValue(tOp, part + n) == -1 ||
tOp->getValue((Uint32)3, buf) == NULL) {
setErrorCode(tOp);
diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp
index a82983fca8c..dff953923fe 100644
--- a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp
+++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp
@@ -792,6 +792,18 @@ NdbEventOperationImpl::receive_event()
p = p->next();
}
}
+ // change the blobHandle's to refer to the new table object.
+ NdbBlob *p = theBlobList;
+ while (p)
+ {
+ int no = p->getColumn()->getColumnNo();
+ NdbColumnImpl *tAttrInfo = at->getColumn(no);
+ DBUG_PRINT("info", ("blob_handle: 0x%lx "
+ "switching column impl 0x%lx -> 0x%lx",
+ (long) p, (long) p->theColumn, (long) tAttrInfo));
+ p->theColumn = tAttrInfo;
+ p = p->next();
+ }
if (tmp_table_impl)
delete tmp_table_impl;
}
diff --git a/storage/ndb/src/ndbapi/NdbIndexOperation.cpp b/storage/ndb/src/ndbapi/NdbIndexOperation.cpp
index fc19bd251d4..921769f09e3 100644
--- a/storage/ndb/src/ndbapi/NdbIndexOperation.cpp
+++ b/storage/ndb/src/ndbapi/NdbIndexOperation.cpp
@@ -85,6 +85,9 @@ int NdbIndexOperation::readTuple(NdbOperation::LockMode lm)
case LM_CommittedRead:
return readTuple();
break;
+ case LM_SimpleRead:
+ return readTuple();
+ break;
default:
return -1;
};
diff --git a/storage/ndb/src/ndbapi/NdbOperation.cpp b/storage/ndb/src/ndbapi/NdbOperation.cpp
index 50531292e40..ddaf5d0b233 100644
--- a/storage/ndb/src/ndbapi/NdbOperation.cpp
+++ b/storage/ndb/src/ndbapi/NdbOperation.cpp
@@ -429,3 +429,9 @@ NdbOperation::getTable() const
{
return m_currentTable;
}
+
+NdbTransaction*
+NdbOperation::getNdbTransaction()
+{
+ return theNdbCon;
+}
diff --git a/storage/ndb/src/ndbapi/NdbOperationDefine.cpp b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
index c9459ff911c..21a6185347e 100644
--- a/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
+++ b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
@@ -131,6 +131,8 @@ NdbOperation::readTuple(NdbOperation::LockMode lm)
case LM_CommittedRead:
return committedRead();
break;
+ case LM_SimpleRead:
+ return simpleRead();
default:
return -1;
};
@@ -185,24 +187,22 @@ NdbOperation::readTupleExclusive()
int
NdbOperation::simpleRead()
{
- /**
- * Currently/still disabled
- */
- return readTuple();
-#if 0
+ NdbTransaction* tNdbCon = theNdbCon;
int tErrorLine = theErrorLine;
if (theStatus == Init) {
theStatus = OperationDefined;
theOperationType = ReadRequest;
theSimpleIndicator = 1;
+ theDirtyIndicator = 0;
theErrorLine = tErrorLine++;
- theLockMode = LM_Read;
+ theLockMode = LM_SimpleRead;
+ m_abortOption = AO_IgnoreError;
+ tNdbCon->theSimpleState = 0;
return 0;
} else {
setErrorCode(4200);
return -1;
}//if
-#endif
}//NdbOperation::simpleRead()
/*****************************************************************************
@@ -338,28 +338,32 @@ NdbOperation::setReadLockMode(LockMode lockMode)
{
/* We only support changing lock mode for read operations at this time. */
assert(theOperationType == ReadRequest || theOperationType == ReadExclusive);
- switch (lockMode)
- {
- case LM_CommittedRead:
- theOperationType= ReadRequest;
- theSimpleIndicator= 1;
- theDirtyIndicator= 1;
- break;
- case LM_Read:
- theNdbCon->theSimpleState= 0;
- theOperationType= ReadRequest;
- theSimpleIndicator= 0;
- theDirtyIndicator= 0;
- break;
- case LM_Exclusive:
- theNdbCon->theSimpleState= 0;
- theOperationType= ReadExclusive;
- theSimpleIndicator= 0;
- theDirtyIndicator= 0;
- break;
- default:
- /* Not supported / invalid. */
- assert(false);
+ switch (lockMode) {
+ case LM_CommittedRead: /* TODO, check theNdbCon->theSimpleState */
+ theOperationType= ReadRequest;
+ theSimpleIndicator= 1;
+ theDirtyIndicator= 1;
+ break;
+ case LM_SimpleRead: /* TODO, check theNdbCon->theSimpleState */
+ theOperationType= ReadRequest;
+ theSimpleIndicator= 1;
+ theDirtyIndicator= 0;
+ break;
+ case LM_Read:
+ theNdbCon->theSimpleState= 0;
+ theOperationType= ReadRequest;
+ theSimpleIndicator= 0;
+ theDirtyIndicator= 0;
+ break;
+ case LM_Exclusive:
+ theNdbCon->theSimpleState= 0;
+ theOperationType= ReadExclusive;
+ theSimpleIndicator= 0;
+ theDirtyIndicator= 0;
+ break;
+ default:
+ /* Not supported / invalid. */
+ assert(false);
}
theLockMode= lockMode;
}
@@ -404,9 +408,8 @@ NdbOperation::getValue_impl(const NdbColumnImpl* tAttrInfo, char* aValue)
return NULL;
}//if
}//if
- Uint32 ah;
- AttributeHeader::init(&ah, tAttrInfo->m_attrId, 0);
- if (insertATTRINFO(ah) != -1) {
+ AttributeHeader ah(tAttrInfo->m_attrId, 0);
+ if (insertATTRINFO(ah.m_value) != -1) {
// Insert Attribute Id into ATTRINFO part.
/************************************************************************
@@ -532,12 +535,11 @@ NdbOperation::setValue( const NdbColumnImpl* tAttrInfo,
tAttrId = tAttrInfo->m_attrId;
m_no_disk_flag &= (tAttrInfo->m_storageType == NDB_STORAGETYPE_DISK ? 0:1);
const char *aValue = aValuePassed;
- Uint32 ahValue;
if (aValue == NULL) {
if (tAttrInfo->m_nullable) {
- AttributeHeader& ah = AttributeHeader::init(&ahValue, tAttrId, 0);
+ AttributeHeader ah(tAttrId, 0);
ah.setNULL();
- insertATTRINFO(ahValue);
+ insertATTRINFO(ah.m_value);
// Insert Attribute Id with the value
// NULL into ATTRINFO part.
DBUG_RETURN(0);
@@ -573,8 +575,8 @@ NdbOperation::setValue( const NdbColumnImpl* tAttrInfo,
// Excluding bits in last word
const Uint32 sizeInWords = sizeInBytes / 4;
- (void) AttributeHeader::init(&ahValue, tAttrId, sizeInBytes);
- insertATTRINFO( ahValue );
+ AttributeHeader ah(tAttrId, sizeInBytes);
+ insertATTRINFO( ah.m_value );
/***********************************************************************
* Check if the pointer of the value passed is aligned on a 4 byte boundary.
diff --git a/storage/ndb/src/ndbapi/NdbOperationExec.cpp b/storage/ndb/src/ndbapi/NdbOperationExec.cpp
index 9fe85265a0c..cd1ac44d82c 100644
--- a/storage/ndb/src/ndbapi/NdbOperationExec.cpp
+++ b/storage/ndb/src/ndbapi/NdbOperationExec.cpp
@@ -24,6 +24,7 @@
#include "Interpreter.hpp"
#include <AttributeHeader.hpp>
#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/TcKeyRef.hpp>
#include <signaldata/KeyInfo.hpp>
#include <signaldata/AttrInfo.hpp>
#include <signaldata/ScanTab.hpp>
@@ -175,12 +176,11 @@ NdbOperation::prepareSend(Uint32 aTC_ConnectPtr,
Uint8 tInterpretIndicator = theInterpretIndicator;
Uint8 tNoDisk = m_no_disk_flag;
-//-------------------------------------------------------------
-// Simple state is set if start and commit is set and it is
-// a read request. Otherwise it is set to zero.
-//-------------------------------------------------------------
+ /**
+ * A dirty read, can not abort the transaction
+ */
Uint8 tReadInd = (theOperationType == ReadRequest);
- Uint8 tSimpleState = tReadInd & tSimpleIndicator;
+ Uint8 tDirtyState = tReadInd & tDirtyIndicator;
tcKeyReq->transId1 = tTransId1;
tcKeyReq->transId2 = tTransId2;
@@ -206,8 +206,8 @@ NdbOperation::prepareSend(Uint32 aTC_ConnectPtr,
tcKeyReq->setOperationType(tReqInfo, tOperationType);
tcKeyReq->setKeyLength(tReqInfo, tTupKeyLen);
- // A simple read is always ignore error
- abortOption = tSimpleState ? (Uint8) AO_IgnoreError : (Uint8) abortOption;
+ // A dirty read is always ignore error
+ abortOption = tDirtyState ? (Uint8) AO_IgnoreError : (Uint8) abortOption;
tcKeyReq->setAbortOption(tReqInfo, abortOption);
m_abortOption = abortOption;
@@ -546,11 +546,17 @@ NdbOperation::receiveTCKEYREF( NdbApiSignal* aSignal)
}//if
setErrorCode(aSignal->readData(4));
+ if (aSignal->getLength() == TcKeyRef::SignalLength)
+ {
+ // Signal may contain additional error data
+ theError.details = (char *) aSignal->readData(5);
+ }
+
theStatus = Finished;
theReceiver.m_received_result_length = ~0;
- // not simple read
- if(! (theOperationType == ReadRequest && theSimpleIndicator))
+ // not dirty read
+ if(! (theOperationType == ReadRequest && theDirtyIndicator))
{
theNdbCon->OpCompleteFailure(this);
return -1;
diff --git a/storage/ndb/src/ndbapi/NdbReceiver.cpp b/storage/ndb/src/ndbapi/NdbReceiver.cpp
index 34b3a14ac6e..5a311bcbefe 100644
--- a/storage/ndb/src/ndbapi/NdbReceiver.cpp
+++ b/storage/ndb/src/ndbapi/NdbReceiver.cpp
@@ -283,7 +283,7 @@ NdbReceiver::execTRANSID_AI(const Uint32* aDataPtr, Uint32 aLength)
Uint32 tmp = m_received_result_length + aLength;
m_received_result_length = tmp;
- return (tmp == exp || (exp > TcKeyConf::SimpleReadBit) ? 1 : 0);
+ return (tmp == exp || (exp > TcKeyConf::DirtyReadBit) ? 1 : 0);
}
int
diff --git a/storage/ndb/src/ndbapi/NdbScanFilter.cpp b/storage/ndb/src/ndbapi/NdbScanFilter.cpp
index fb47772fdea..25f74ce71a4 100644
--- a/storage/ndb/src/ndbapi/NdbScanFilter.cpp
+++ b/storage/ndb/src/ndbapi/NdbScanFilter.cpp
@@ -14,11 +14,15 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <NdbScanFilter.hpp>
+#include <Ndb.hpp>
#include <NdbOperation.hpp>
#include "NdbDictionaryImpl.hpp"
#include <Vector.hpp>
#include <NdbOut.hpp>
#include <Interpreter.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include "NdbApiSignal.hpp"
+#include "NdbUtil.hpp"
#ifdef VM_TRACE
#include <NdbEnv.h>
@@ -52,14 +56,37 @@ public:
int cond_col_const(Interpreter::BinaryCondition, Uint32 attrId,
const void * value, Uint32 len);
+
+ bool m_abort_on_too_large;
+
+ NdbOperation::OperationStatus m_initial_op_status;
+ Uint32 m_initial_AI_size;
+ Uint32 m_max_size;
+
+ Uint32 get_size() {
+ assert(m_operation->theTotalCurrAI_Len >= m_initial_AI_size);
+ return m_operation->theTotalCurrAI_Len - m_initial_AI_size;
+ }
+ bool check_size() {
+ if (get_size() <= m_max_size)
+ return true;
+ handle_filter_too_large();
+ return false;
+ }
+ void handle_filter_too_large();
+
+ NdbError m_error;
};
const Uint32 LabelExit = ~0;
-NdbScanFilter::NdbScanFilter(class NdbOperation * op)
+NdbScanFilter::NdbScanFilter(class NdbOperation * op,
+ bool abort_on_too_large,
+ Uint32 max_size)
: m_impl(* new NdbScanFilterImpl())
{
+ DBUG_ENTER("NdbScanFilter::NdbScanFilter");
m_impl.m_current.m_group = (NdbScanFilter::Group)0;
m_impl.m_current.m_popCount = 0;
m_impl.m_current.m_ownLabel = 0;
@@ -69,6 +96,21 @@ NdbScanFilter::NdbScanFilter(class NdbOperation * op)
m_impl.m_latestAttrib = ~0;
m_impl.m_operation = op;
m_impl.m_negative = 0;
+
+ DBUG_PRINT("info", ("op status: %d tot AI: %u in curr: %u",
+ op->theStatus,
+ op->theTotalCurrAI_Len, op->theAI_LenInCurrAI));
+
+ m_impl.m_abort_on_too_large = abort_on_too_large;
+
+ m_impl.m_initial_op_status = op->theStatus;
+ m_impl.m_initial_AI_size = op->theTotalCurrAI_Len;
+ if (max_size > NDB_MAX_SCANFILTER_SIZE_IN_WORDS)
+ max_size = NDB_MAX_SCANFILTER_SIZE_IN_WORDS;
+ m_impl.m_max_size = max_size;
+
+ m_impl.m_error.code = 0;
+ DBUG_VOID_RETURN;
}
NdbScanFilter::~NdbScanFilter(){
@@ -200,30 +242,38 @@ NdbScanFilter::end(){
switch(tmp.m_group){
case NdbScanFilter::AND:
if(tmp.m_trueLabel == (Uint32)~0){
- m_impl.m_operation->interpret_exit_ok();
+ if (m_impl.m_operation->interpret_exit_ok() == -1)
+ return -1;
} else {
- m_impl.m_operation->branch_label(tmp.m_trueLabel);
+ if (m_impl.m_operation->branch_label(tmp.m_trueLabel) == -1)
+ return -1;
}
break;
case NdbScanFilter::NAND:
if(tmp.m_trueLabel == (Uint32)~0){
- m_impl.m_operation->interpret_exit_nok();
+ if (m_impl.m_operation->interpret_exit_nok() == -1)
+ return -1;
} else {
- m_impl.m_operation->branch_label(tmp.m_falseLabel);
+ if (m_impl.m_operation->branch_label(tmp.m_falseLabel) == -1)
+ return -1;
}
break;
case NdbScanFilter::OR:
if(tmp.m_falseLabel == (Uint32)~0){
- m_impl.m_operation->interpret_exit_nok();
+ if (m_impl.m_operation->interpret_exit_nok() == -1)
+ return -1;
} else {
- m_impl.m_operation->branch_label(tmp.m_falseLabel);
+ if (m_impl.m_operation->branch_label(tmp.m_falseLabel) == -1)
+ return -1;
}
break;
case NdbScanFilter::NOR:
if(tmp.m_falseLabel == (Uint32)~0){
- m_impl.m_operation->interpret_exit_ok();
+ if (m_impl.m_operation->interpret_exit_ok() == -1)
+ return -1;
} else {
- m_impl.m_operation->branch_label(tmp.m_trueLabel);
+ if (m_impl.m_operation->branch_label(tmp.m_trueLabel) == -1)
+ return -1;
}
break;
default:
@@ -231,24 +281,29 @@ NdbScanFilter::end(){
return -1;
}
- m_impl.m_operation->def_label(tmp.m_ownLabel);
+ if (m_impl.m_operation->def_label(tmp.m_ownLabel) == -1)
+ return -1;
if(m_impl.m_stack.size() == 0){
switch(tmp.m_group){
case NdbScanFilter::AND:
case NdbScanFilter::NOR:
- m_impl.m_operation->interpret_exit_nok();
+ if (m_impl.m_operation->interpret_exit_nok() == -1)
+ return -1;
break;
case NdbScanFilter::OR:
case NdbScanFilter::NAND:
- m_impl.m_operation->interpret_exit_ok();
+ if (m_impl.m_operation->interpret_exit_ok() == -1)
+ return -1;
break;
default:
m_impl.m_operation->setErrorCodeAbort(4260);
return -1;
}
}
-
+
+ if (!m_impl.check_size())
+ return -1;
return 0;
}
@@ -261,10 +316,16 @@ NdbScanFilter::istrue(){
}
if(m_impl.m_current.m_trueLabel == (Uint32)~0){
- return m_impl.m_operation->interpret_exit_ok();
+ if (m_impl.m_operation->interpret_exit_ok() == -1)
+ return -1;
} else {
- return m_impl.m_operation->branch_label(m_impl.m_current.m_trueLabel);
+ if (m_impl.m_operation->branch_label(m_impl.m_current.m_trueLabel) == -1)
+ return -1;
}
+
+ if (!m_impl.check_size())
+ return -1;
+ return 0;
}
int
@@ -276,12 +337,22 @@ NdbScanFilter::isfalse(){
}
if(m_impl.m_current.m_falseLabel == (Uint32)~0){
- return m_impl.m_operation->interpret_exit_nok();
+ if (m_impl.m_operation->interpret_exit_nok() == -1)
+ return -1;
} else {
- return m_impl.m_operation->branch_label(m_impl.m_current.m_falseLabel);
+ if (m_impl.m_operation->branch_label(m_impl.m_current.m_falseLabel) == -1)
+ return -1;
}
+
+ if (!m_impl.check_size())
+ return -1;
+ return 0;
}
+NdbOperation *
+NdbScanFilter::getNdbOperation(){
+ return m_impl.m_operation;
+}
#define action(x, y, z)
@@ -330,7 +401,11 @@ NdbScanFilterImpl::cond_col(Interpreter::UnaryCondition op, Uint32 AttrId){
}
Branch1 branch = table2[op].m_branches[m_current.m_group];
- (m_operation->* branch)(AttrId, m_current.m_ownLabel);
+ if ((m_operation->* branch)(AttrId, m_current.m_ownLabel) == -1)
+ return -1;
+
+ if (!check_size())
+ return -1;
return 0;
}
@@ -463,8 +538,12 @@ NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition op,
return -1;
}
- int ret = (m_operation->* branch)(AttrId, value, len, false, m_current.m_ownLabel);
- return ret;
+ if ((m_operation->* branch)(AttrId, value, len, false, m_current.m_ownLabel) == -1)
+ return -1;
+
+ if (!check_size())
+ return -1;
+ return 0;
}
int
@@ -490,7 +569,130 @@ NdbScanFilter::cmp(BinaryCondition cond, int ColId,
return m_impl.cond_col_const(Interpreter::NOT_LIKE, ColId, val, len);
}
return -1;
-}
+}
+
+void
+NdbScanFilterImpl::handle_filter_too_large()
+{
+ DBUG_ENTER("NdbScanFilterImpl::handle_filter_too_large");
+
+ NdbOperation* const op = m_operation;
+ m_error.code = NdbScanFilter::FilterTooLarge;
+ if (m_abort_on_too_large)
+ op->setErrorCodeAbort(m_error.code);
+
+ /*
+ * Possible interpreted parts at this point are:
+ *
+ * 1. initial read
+ * 2. interpreted program
+ *
+ * It is assumed that NdbScanFilter has created all of 2
+ * so that we don't have to save interpreter state.
+ */
+
+ const Uint32 size = get_size();
+ assert(size != 0);
+
+ // new ATTRINFO size
+ const Uint32 new_size = m_initial_AI_size;
+
+ // find last signal for new size
+ assert(op->theFirstATTRINFO != NULL);
+ NdbApiSignal* lastSignal = op->theFirstATTRINFO;
+ Uint32 n = 0;
+ while (n + AttrInfo::DataLength < new_size) {
+ lastSignal = lastSignal->next();
+ assert(lastSignal != NULL);
+ n += AttrInfo::DataLength;
+ }
+ assert(n < size);
+
+ // release remaining signals
+ NdbApiSignal* tSignal = lastSignal->next();
+ op->theNdb->releaseSignalsInList(&tSignal);
+ lastSignal->next(NULL);
+
+ // length of lastSignal
+ const Uint32 new_curr = AttrInfo::HeaderLength + new_size - n;
+ assert(new_curr <= 25);
+
+ DBUG_PRINT("info", ("op status: %d->%d tot AI: %u->%u in curr: %u->%u",
+ op->theStatus, m_initial_op_status,
+ op->theTotalCurrAI_Len, new_size,
+ op->theAI_LenInCurrAI, new_curr));
+
+ // reset op state
+ op->theStatus = m_initial_op_status;
+
+ // reset interpreter state to initial
+
+ NdbBranch* tBranch = op->theFirstBranch;
+ while (tBranch != NULL) {
+ NdbBranch* tmp = tBranch;
+ tBranch = tBranch->theNext;
+ op->theNdb->releaseNdbBranch(tmp);
+ }
+ op->theFirstBranch = NULL;
+ op->theLastBranch = NULL;
+
+ NdbLabel* tLabel = op->theFirstLabel;
+ while (tLabel != NULL) {
+ NdbLabel* tmp = tLabel;
+ tLabel = tLabel->theNext;
+ op->theNdb->releaseNdbLabel(tmp);
+ }
+ op->theFirstLabel = NULL;
+ op->theLastLabel = NULL;
+
+ NdbCall* tCall = op->theFirstCall;
+ while (tCall != NULL) {
+ NdbCall* tmp = tCall;
+ tCall = tCall->theNext;
+ op->theNdb->releaseNdbCall(tmp);
+ }
+ op->theFirstCall = NULL;
+ op->theLastCall = NULL;
+
+ NdbSubroutine* tSubroutine = op->theFirstSubroutine;
+ while (tSubroutine != NULL) {
+ NdbSubroutine* tmp = tSubroutine;
+ tSubroutine = tSubroutine->theNext;
+ op->theNdb->releaseNdbSubroutine(tmp);
+ }
+ op->theFirstSubroutine = NULL;
+ op->theLastSubroutine = NULL;
+
+ op->theNoOfLabels = 0;
+ op->theNoOfSubroutines = 0;
+
+ // reset AI size
+ op->theTotalCurrAI_Len = new_size;
+ op->theAI_LenInCurrAI = new_curr;
+
+ // reset signal pointers
+ op->theCurrentATTRINFO = lastSignal;
+ op->theATTRINFOptr = &lastSignal->getDataPtrSend()[new_curr];
+
+ // interpreter sizes are set later somewhere
+
+ DBUG_VOID_RETURN;
+}
+
+static void
+update(const NdbError & _err){
+ NdbError & error = (NdbError &) _err;
+ ndberror_struct ndberror = (ndberror_struct)error;
+ ndberror_update(&ndberror);
+ error = NdbError(ndberror);
+}
+
+const NdbError &
+NdbScanFilter::getNdbError() const
+{
+ update(m_impl.m_error);
+ return m_impl.m_error;
+}
#if 0
diff --git a/storage/ndb/src/ndbapi/NdbScanOperation.cpp b/storage/ndb/src/ndbapi/NdbScanOperation.cpp
index 89782453a72..96a3ce4332e 100644
--- a/storage/ndb/src/ndbapi/NdbScanOperation.cpp
+++ b/storage/ndb/src/ndbapi/NdbScanOperation.cpp
@@ -237,6 +237,7 @@ NdbScanOperation::setReadLockMode(LockMode lockMode)
lockHoldMode= false;
readCommitted= true;
break;
+ case LM_SimpleRead:
case LM_Read:
lockExcl= false;
lockHoldMode= true;
@@ -872,6 +873,10 @@ NdbScanOperation::doSendScan(int aProcessorId)
// sending it. This could not be done in openScan because
// we created the ATTRINFO signals after the SCAN_TABREQ signal.
ScanTabReq * const req = CAST_PTR(ScanTabReq, tSignal->getDataPtrSend());
+ if (unlikely(theTotalCurrAI_Len > ScanTabReq::MaxTotalAttrInfo)) {
+ setErrorCode(4257);
+ return -1;
+ }
req->attrLenKeyLen = (tupKeyLen << 16) | theTotalCurrAI_Len;
Uint32 tmp = req->requestInfo;
ScanTabReq::setDistributionKeyFlag(tmp, theDistrKeyIndicator_);
@@ -1335,29 +1340,41 @@ NdbIndexScanOperation::readTuples(LockMode lm,
if(insertATTRINFO(word) == -1)
res = -1;
}
- if(!res && order_by){
- m_ordered = true;
+ if (!res)
+ {
+ /**
+ * Note that it is valid to have order_desc true and order_by false.
+ *
+ * This means that there will be no merge sort among partitions, but
+ * each partition will still be returned in descending sort order.
+ *
+ * This is useful eg. if it is known that the scan spans only one
+ * partition.
+ */
if (order_desc) {
m_descending = true;
ScanTabReq * req = CAST_PTR(ScanTabReq, theSCAN_TABREQ->getDataPtrSend());
ScanTabReq::setDescendingFlag(req->requestInfo, true);
}
- Uint32 cnt = m_accessTable->getNoOfColumns() - 1;
- m_sort_columns = cnt; // -1 for NDB$NODE
- m_current_api_receiver = m_sent_receivers_count;
- m_api_receivers_count = m_sent_receivers_count;
+ if (order_by) {
+ m_ordered = true;
+ Uint32 cnt = m_accessTable->getNoOfColumns() - 1;
+ m_sort_columns = cnt; // -1 for NDB$NODE
+ m_current_api_receiver = m_sent_receivers_count;
+ m_api_receivers_count = m_sent_receivers_count;
- m_sort_columns = cnt;
- for(Uint32 i = 0; i<cnt; i++){
- const NdbColumnImpl* key = m_accessTable->m_index->m_columns[i];
- const NdbColumnImpl* col = m_currentTable->getColumn(key->m_keyInfoPos);
- NdbRecAttr* tmp = NdbScanOperation::getValue_impl(col, (char*)-1);
- UintPtr newVal = UintPtr(tmp);
- theTupleKeyDefined[i][0] = FAKE_PTR;
- theTupleKeyDefined[i][1] = (newVal & 0xFFFFFFFF);
+ m_sort_columns = cnt;
+ for(Uint32 i = 0; i<cnt; i++){
+ const NdbColumnImpl* key = m_accessTable->m_index->m_columns[i];
+ const NdbColumnImpl* col = m_currentTable->getColumn(key->m_keyInfoPos);
+ NdbRecAttr* tmp = NdbScanOperation::getValue_impl(col, (char*)-1);
+ UintPtr newVal = UintPtr(tmp);
+ theTupleKeyDefined[i][0] = FAKE_PTR;
+ theTupleKeyDefined[i][1] = (newVal & 0xFFFFFFFF);
#if (SIZEOF_CHARP == 8)
- theTupleKeyDefined[i][2] = (newVal >> 32);
+ theTupleKeyDefined[i][2] = (newVal >> 32);
#endif
+ }
}
}
m_this_bound_start = 0;
diff --git a/storage/ndb/src/ndbapi/NdbTransaction.cpp b/storage/ndb/src/ndbapi/NdbTransaction.cpp
index 55c6f0f4b99..17c7188bff2 100644
--- a/storage/ndb/src/ndbapi/NdbTransaction.cpp
+++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp
@@ -30,6 +30,7 @@
#include <signaldata/TcCommit.hpp>
#include <signaldata/TcKeyFailConf.hpp>
#include <signaldata/TcHbRep.hpp>
+#include <signaldata/TcRollbackRep.hpp>
/*****************************************************************************
NdbTransaction( Ndb* aNdb );
@@ -1729,6 +1730,8 @@ Remark: Handles the reception of the ROLLBACKREP signal.
int
NdbTransaction::receiveTCROLLBACKREP( NdbApiSignal* aSignal)
{
+ DBUG_ENTER("NdbTransaction::receiveTCROLLBACKREP");
+
/****************************************************************************
Check that we are expecting signals from this transaction and that it doesn't
belong to a transaction already completed. Simply ignore messages from other
@@ -1736,6 +1739,11 @@ transactions.
****************************************************************************/
if(checkState_TransId(aSignal->getDataPtr() + 1)){
theError.code = aSignal->readData(4);// Override any previous errors
+ if (aSignal->getLength() == TcRollbackRep::SignalLength)
+ {
+ // Signal may contain additional error data
+ theError.details = (char *) aSignal->readData(5);
+ }
/**********************************************************************/
/* A serious error has occured. This could be due to deadlock or */
@@ -1747,14 +1755,14 @@ transactions.
theCompletionStatus = CompletedFailure;
theCommitStatus = Aborted;
theReturnStatus = ReturnFailure;
- return 0;
+ DBUG_RETURN(0);
} else {
#ifdef NDB_NO_DROPPED_SIGNAL
abort();
#endif
}
- return -1;
+ DBUG_RETURN(-1);
}//NdbTransaction::receiveTCROLLBACKREP()
/*******************************************************************************
@@ -1787,8 +1795,8 @@ from other transactions.
const Uint32 tAttrInfoLen = *tPtr++;
if (tOp && tOp->checkMagicNumber()) {
Uint32 done = tOp->execTCOPCONF(tAttrInfoLen);
- if(tAttrInfoLen > TcKeyConf::SimpleReadBit){
- Uint32 node = tAttrInfoLen & (~TcKeyConf::SimpleReadBit);
+ if(tAttrInfoLen > TcKeyConf::DirtyReadBit){
+ Uint32 node = tAttrInfoLen & (~TcKeyConf::DirtyReadBit);
NdbNodeBitmask::set(m_db_nodes, node);
if(NdbNodeBitmask::get(m_failed_db_nodes, node) && !done)
{
@@ -2182,7 +2190,7 @@ NdbTransaction::report_node_failure(Uint32 id){
* 4) X X
*/
NdbOperation* tmp = theFirstExecOpInList;
- const Uint32 len = TcKeyConf::SimpleReadBit | id;
+ const Uint32 len = TcKeyConf::DirtyReadBit | id;
Uint32 tNoComp = theNoOfOpCompleted;
Uint32 tNoSent = theNoOfOpSent;
Uint32 count = 0;
diff --git a/storage/ndb/src/ndbapi/TransporterFacade.cpp b/storage/ndb/src/ndbapi/TransporterFacade.cpp
index eabfc6bc371..22eee859ef3 100644
--- a/storage/ndb/src/ndbapi/TransporterFacade.cpp
+++ b/storage/ndb/src/ndbapi/TransporterFacade.cpp
@@ -1501,9 +1501,9 @@ void PollGuard::unlock_and_signal()
if (t_signal_cond_waiter)
t_signal_cond_waiter->set_poll_owner(true);
}
- m_tp->unlock_mutex();
if (t_signal_cond_waiter)
t_signal_cond_waiter->cond_signal();
+ m_tp->unlock_mutex();
m_locked=false;
}
diff --git a/storage/ndb/src/ndbapi/ndberror.c b/storage/ndb/src/ndbapi/ndberror.c
index 09ea3553c40..2ebf09c42ce 100644
--- a/storage/ndb/src/ndbapi/ndberror.c
+++ b/storage/ndb/src/ndbapi/ndberror.c
@@ -169,7 +169,7 @@ ErrorBundle ErrorCodes[] = {
{ 219, DMEC, TR, "219" },
{ 233, DMEC, TR,
"Out of operation records in transaction coordinator (increase MaxNoOfConcurrentOperations)" },
- { 275, DMEC, TR, "275" },
+ { 275, DMEC, TR, "Out of transaction records for complete phase (increase MaxNoOfConcurrentTransactions)" },
{ 279, DMEC, TR, "Out of transaction markers in transaction coordinator" },
{ 414, DMEC, TR, "414" },
{ 418, DMEC, TR, "Out of transaction buffers in LQH" },
@@ -625,6 +625,7 @@ ErrorBundle ErrorCodes[] = {
{ 4273, DMEC, IE, "No blob table in dict cache" },
{ 4274, DMEC, IE, "Corrupted main table PK in blob operation" },
{ 4275, DMEC, AE, "The blob method is incompatible with operation type or lock mode" },
+ { 4294, DMEC, AE, "Scan filter is too large, discarded" },
{ 2810, DMEC, TR, "No space left on the device" },
{ 2815, DMEC, TR, "Error in reading files, please check file system" },
@@ -768,8 +769,6 @@ ndberror_update(ndberror_struct * error){
if(!found){
error->status = ST_U;
}
-
- error->details = 0;
}
#if CHECK_ERRORCODES
diff --git a/storage/ndb/test/include/DbUtil.hpp b/storage/ndb/test/include/DbUtil.hpp
new file mode 100755
index 00000000000..5ca5fca290c
--- /dev/null
+++ b/storage/ndb/test/include/DbUtil.hpp
@@ -0,0 +1,131 @@
+/* Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+// dbutil.h: interface for the database utilities class.
+// Supplies a database to the test application
+
+#ifndef DBUTIL_HPP
+#define DBUTIL_HPP
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <mysql.h>
+//include "rand.h"
+#include <stdlib.h>
+#include "BaseString.hpp"
+#include "NDBT.hpp"
+
+//#define DEBUG
+#define DIE_UNLESS(expr) \
+ ((void) ((expr) ? 0 : (Die(__FILE__, __LINE__, #expr), 0)))
+#define DIE(expr) \
+ Die(__FILE__, __LINE__, #expr)
+#define myerror(msg) printError(msg)
+#define mysterror(stmt, msg) printStError(stmt, msg)
+#define CheckStmt(stmt) \
+{ \
+if ( stmt == 0) \
+ myerror(NULL); \
+DIE_UNLESS(stmt != 0); \
+}
+
+#define check_execute(stmt, r) \
+{ \
+if (r) \
+ mysterror(stmt, NULL); \
+DIE_UNLESS(r == 0);\
+}
+
+#define DBU_TRUE 1
+#define DBU_FALSE 0
+#define DBU_FAILED 1
+#define DBU_OK 0
+
+class DbUtil
+{
+public:
+
+ /* Deprecated, see DbUtil(dbname, suffix) */
+ DbUtil(const char * databaseName);
+ DbUtil(const char* dbname, const char* suffix = NULL);
+ ~DbUtil();
+
+ /* Deprecated, see connect() */
+ void databaseLogin(const char * system,
+ const char * usr,
+ const char * password,
+ unsigned int portIn,
+ const char * sockIn,
+ bool transactional);
+
+ const char * getDbName() {return m_dbname.c_str();};
+ const char * getUser() {return m_user.c_str();};
+ const char * getPassword(){return m_pass.c_str();};
+ const char * getHost() {return m_host.c_str();};
+ const char * getSocket() {return m_socket.c_str();};
+ const char * getServerType(){return mysql_get_server_info(mysql);};
+ const char * getError();
+
+ MYSQL * getMysql(){return mysql;};
+ MYSQL_STMT * STDCALL mysqlSimplePrepare(const char *query);
+
+ void databaseLogout();
+ void mysqlCloseStmHandle(MYSQL_STMT *my_stmt);
+
+ int connect();
+ int selectDb();
+ int selectDb(const char *);
+ int createDb(BaseString&);
+ int doQuery(BaseString&);
+ int doQuery(const char *);
+ int getErrorNumber();
+
+ unsigned long selectCountTable(const char * table);
+
+private:
+
+ bool m_connected;
+
+ BaseString m_host; // Computer to connect to
+ BaseString m_user; // MySQL User
+ BaseString m_pass; // MySQL User Password
+ BaseString m_dbname; // Database to use
+ BaseString m_socket; // MySQL Server Unix Socket
+ BaseString default_file;
+ BaseString default_group;
+
+ unsigned int m_port; // MySQL Server port
+
+ MYSQL * mysql;
+ MYSQL_RES * m_result;
+ MYSQL_ROW m_row;
+
+ void setDbName(const char * name){m_dbname.assign(name);};
+ void setUser(const char * user_name){m_user.assign(user_name);};
+ void setPassword(const char * password){m_pass.assign(password);};
+ void setHost(const char * system){m_host.assign(system);};
+ void setPort(unsigned int portIn){m_port=portIn;};
+ void setSocket(const char * sockIn){m_socket.assign(sockIn);};
+ void printError(const char *msg);
+ void printStError(MYSQL_STMT *stmt, const char *msg);
+ void die(const char *file, int line, const char *expr); // stop program
+
+};
+#endif
+
diff --git a/storage/ndb/test/include/HugoTransactions.hpp b/storage/ndb/test/include/HugoTransactions.hpp
index e2b12f261a8..e8f7b33e0ed 100644
--- a/storage/ndb/test/include/HugoTransactions.hpp
+++ b/storage/ndb/test/include/HugoTransactions.hpp
@@ -36,6 +36,16 @@ public:
int updateValue = 0,
bool abort = false);
+ int loadTableStartFrom(Ndb*,
+ int startFrom,
+ int records,
+ int batch = 512,
+ bool allowConstraintViolation = true,
+ int doSleep = 0,
+ bool oneTrans = false,
+ int updateValue = 0,
+ bool abort = false);
+
int scanReadRecords(Ndb*,
int records,
int abort = 0,
@@ -56,6 +66,11 @@ public:
int batchsize = 1,
NdbOperation::LockMode = NdbOperation::LM_Read);
+ int scanUpdateRecords(Ndb*, NdbScanOperation::ScanFlag,
+ int records,
+ int abort = 0,
+ int parallelism = 0);
+
int scanUpdateRecords(Ndb*,
int records,
int abort = 0,
@@ -90,9 +105,12 @@ public:
int records,
int percentToLock = 1,
int lockTime = 1000);
+
int fillTable(Ndb*,
int batch=512);
+ int fillTableStartFrom(Ndb*, int startFrom, int batch=512);
+
/**
* Reading using UniqHashIndex with key = pk
*/
diff --git a/storage/ndb/test/include/UtilTransactions.hpp b/storage/ndb/test/include/UtilTransactions.hpp
index 75bbcd9c776..193398c3da2 100644
--- a/storage/ndb/test/include/UtilTransactions.hpp
+++ b/storage/ndb/test/include/UtilTransactions.hpp
@@ -30,6 +30,11 @@ public:
int closeTransaction(Ndb*);
int clearTable(Ndb*,
+ NdbScanOperation::ScanFlag,
+ int records = 0,
+ int parallelism = 0);
+
+ int clearTable(Ndb*,
int records = 0,
int parallelism = 0);
diff --git a/storage/ndb/test/ndbapi/acrt/NdbRepStress.cpp b/storage/ndb/test/ndbapi/acrt/NdbRepStress.cpp
new file mode 100644
index 00000000000..775e7ec6288
--- /dev/null
+++ b/storage/ndb/test/ndbapi/acrt/NdbRepStress.cpp
@@ -0,0 +1,478 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <DbUtil.hpp>
+#include <mysql.h>
+
+/*
+Will include restart testing in future phases
+#include <NdbRestarter.hpp>
+#include <NdbRestarts.hpp>
+*/
+
+/**** TOOL SECTION ****/
+
+static uint
+urandom()
+{
+ uint r = (uint)random();
+ return r;
+}
+
+static uint
+urandom(uint m)
+{
+ if (m == 0)
+ return NDBT_OK;
+ uint r = urandom();
+ r = r % m;
+ return r;
+}
+
+#define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb()
+/*
+*/
+
+int
+syncSlaveWithMaster()
+{
+ /*
+ We need to look at the MAX epoch of the
+ mysql.ndb_binlog_index table so we will
+ know when the slave has caught up
+ */
+
+ MYSQL_RES * result;
+ MYSQL_ROW row;
+ unsigned int masterEpoch = 0;
+ unsigned int slaveEpoch = 0;
+ unsigned int slaveEpochOld = 0;
+ int maxLoops = 100;
+ int loopCnt = 0;
+
+ //Create a DbUtil object for the master
+ DbUtil master("mysql","");
+
+ //Login to Master
+ if (!master.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ //Get max epoch from master
+ if(master.doQuery("SELECT MAX(epoch) FROM mysql.ndb_binlog_index"))
+ {
+ return NDBT_FAILED;
+ }
+ result = mysql_use_result(master.getMysql());
+ row = mysql_fetch_row(result);
+ masterEpoch = atoi(row[0]);
+ mysql_free_result(result);
+
+ /*
+ Now we will pull current epoch from slave. If not the
+ same as master, we will continue to retrieve the epoch
+ and compare until it matches or we reach the max loops
+ allowed.
+ */
+
+ //Create a dbutil object for the slave
+ DbUtil slave("mysql",".slave");
+
+ //Login to slave
+ if (!slave.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ while(slaveEpoch != masterEpoch && loopCnt < maxLoops)
+ {
+ if(slave.doQuery("SELECT epoch FROM mysql.ndb_apply_status"))
+ {
+ return NDBT_FAILED;
+ }
+ result = mysql_use_result(slave.getMysql());
+ row = mysql_fetch_row(result);
+ slaveEpoch = atoi(row[0]);
+ mysql_free_result(result);
+
+ if(slaveEpoch != slaveEpochOld)
+ {
+ slaveEpochOld = slaveEpoch;
+ if(loopCnt > 0)
+ loopCnt--;
+ sleep(3);
+ }
+ else
+ {
+ sleep(1);
+ loopCnt++;
+ }
+ }
+
+ if(slaveEpoch != masterEpoch)
+ {
+ g_err << "Slave not in sync with master!" << endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int
+verifySlaveLoad(BaseString *table)
+{
+ BaseString sqlStm;
+ BaseString db;
+ MYSQL_RES * result;
+ MYSQL_ROW row;
+ unsigned int masterCount = 0;
+ unsigned int slaveCount = 0;
+
+ db.assign("TEST_DB");
+ sqlStm.assfmt("SELECT COUNT(*) FROM %s", table);
+
+ //First thing to do is sync slave
+ if(syncSlaveWithMaster())
+ {
+ g_err << "Verify Load -> Syncing with slave failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ //Now that slave is sync we can verify load
+ DbUtil master(db.c_str()," ");
+
+ //Login to Master
+ if (!master.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ if(master.doQuery(sqlStm.c_str()))
+ {
+ return NDBT_FAILED;
+ }
+ result = mysql_use_result(master.getMysql());
+ row = mysql_fetch_row(result);
+ masterCount = atoi(row[0]);
+ mysql_free_result(result);
+
+ //Create a DB Object for slave
+ DbUtil slave(db.c_str(),".slave");
+
+ //Login to slave
+ if (!slave.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ if(slave.doQuery(sqlStm.c_str()))
+ {
+ return NDBT_FAILED;
+ }
+ result = mysql_use_result(slave.getMysql());
+ row = mysql_fetch_row(result);
+ slaveCount = atoi(row[0]);
+ mysql_free_result(result);
+
+ if(slaveCount != masterCount)
+ {
+ g_err << "Verify Load -> Slave Count != Master Count "
+ << endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int
+createTEST_DB(NDBT_Context* ctx, NDBT_Step* step)
+{
+ BaseString cdb;
+ cdb.assign("TEST_DB");
+
+ //Create a dbutil object
+ DbUtil master("mysql","");
+
+ if (master.connect())
+ {
+ if (master.createDb(cdb) == NDBT_OK)
+ {
+ return NDBT_OK;
+ }
+ }
+ return NDBT_FAILED;
+}
+
+int
+dropTEST_DB(NDBT_Context* ctx, NDBT_Step* step)
+{
+ //Create an SQL Object
+ DbUtil master("mysql","");
+
+ //Login to Master
+ if (!master.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ if(master.doQuery("DROP DATABASE TEST_DB") != NDBT_OK)
+ {
+ return NDBT_FAILED;
+ }
+
+ if(syncSlaveWithMaster() != NDBT_OK)
+ {
+ g_err << "Drop DB -> Syncing with slave failed"
+ << endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int
+verifySlave(BaseString& sqlStm, BaseString& db)
+{
+ MYSQL_RES* resource;
+ MYSQL_ROW row;
+ float masterSum;
+ float slaveSum;
+
+ //Create SQL Objects
+ DbUtil master(db.c_str(),"");
+ DbUtil slave(db.c_str(),".slave");
+
+ if(syncSlaveWithMaster() != NDBT_OK)
+ {
+ g_err << "Verify Slave rep1 -> Syncing with slave failed"
+ << endl;
+ return NDBT_FAILED;
+ }
+
+ //Login to Master
+ if (!master.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ if(master.doQuery(sqlStm.c_str()) != NDBT_OK)
+ {
+ return NDBT_FAILED;
+ }
+ resource = mysql_use_result(master.getMysql());
+ row = mysql_fetch_row(resource);
+ masterSum = atoi(row[0]);
+ mysql_free_result(resource);
+
+ //Login to slave
+ if (!slave.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ if(slave.doQuery(sqlStm.c_str()) != NDBT_OK)
+ {
+ return NDBT_FAILED;
+ }
+ resource = mysql_use_result(slave.getMysql());
+ row = mysql_fetch_row(resource);
+ slaveSum = atoi(row[0]);
+ mysql_free_result(resource);
+
+ if(masterSum != slaveSum)
+ {
+ g_err << "VerifySlave -> masterSum != slaveSum..." << endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+/**** Test Section ****/
+
+int
+createDB(NDBT_Context* ctx, NDBT_Step* step)
+{
+ BaseString cdb;
+ cdb.assign("TEST_DB");
+
+ //Create a dbutil object
+ DbUtil master("mysql","");
+
+ if (master.connect())
+ {
+ if (master.createDb(cdb) == NDBT_OK)
+ {
+ return NDBT_OK;
+ }
+ }
+ return NDBT_FAILED;
+}
+
+int
+createTable_rep1(NDBT_Context* ctx, NDBT_Step* step)
+{
+ BaseString table;
+ BaseString db;
+
+ table.assign("rep1");
+ db.assign("TEST_DB");
+
+ //Ensure slave is up and ready
+ if(syncSlaveWithMaster() != NDBT_OK)
+ {
+ g_err << "Create Table -> Syncing with slave failed"
+ << endl;
+ return NDBT_FAILED;
+ }
+
+ //Create an SQL Object
+ DbUtil master(db.c_str(),"");
+
+ //Login to Master
+ if (!master.connect())
+ {
+ return NDBT_FAILED;
+ }
+
+ if (master.doQuery("CREATE TABLE rep1 (c1 MEDIUMINT NOT NULL AUTO_INCREMENT,"
+ " c2 FLOAT, c3 CHAR(5), c4 bit(8), c5 FLOAT, c6 INT,"
+ " c7 INT, PRIMARY KEY (c1))ENGINE=NDB"))
+ {
+ return NDBT_FAILED;
+ }
+ ctx->setProperty("TABLES",table.c_str());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ if (hugoTrans.loadTable(GETNDB(step), ctx->getNumRecords(), 1, true, 0) != NDBT_OK)
+ {
+ g_err << "Create Table -> Load failed!" << endl;
+ return NDBT_FAILED;
+ }
+
+ if(verifySlaveLoad(&table)!= NDBT_OK)
+ {
+ g_err << "Create Table -> Failed on verify slave load!"
+ << endl;
+ return NDBT_FAILED;
+ }
+ //else everything is okay
+ return NDBT_OK;
+}
+
+int
+stressNDB_rep1(NDBT_Context* ctx, NDBT_Step* step)
+{
+ const NdbDictionary::Table * table= ctx->getTab();
+ HugoTransactions hugoTrans(* table);
+ while(!ctx->isTestStopped())
+ {
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), ctx->getNumRecords(), 1, 30) != 0)
+ {
+ g_err << "pkUpdate Failed!" << endl;
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.scanUpdateRecords(GETNDB(step), ctx->getNumRecords(), 1, 30) != 0)
+ {
+ g_err << "scanUpdate Failed!" << endl;
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+int
+stressSQL_rep1(NDBT_Context* ctx, NDBT_Step* step)
+{
+ BaseString sqlStm;
+
+ DbUtil master("TEST_DB","");
+ int loops = ctx->getNumLoops();
+ uint record = 0;
+
+ //Login to Master
+ if (!master.connect())
+ {
+ ctx->stopTest();
+ return NDBT_FAILED;
+ }
+
+ for (int j= 0; loops == 0 || j < loops; j++)
+ {
+ record = urandom(ctx->getNumRecords());
+ sqlStm.assfmt("UPDATE TEST_DB.rep1 SET c2 = 33.3221 where c1 = %u", record);
+ if(master.doQuery(sqlStm.c_str()))
+ {
+ return NDBT_FAILED;
+ }
+ }
+ ctx->stopTest();
+ return NDBT_OK;
+}
+
+int
+verifySlave_rep1(NDBT_Context* ctx, NDBT_Step* step)
+{
+ BaseString sql;
+ BaseString db;
+
+ sql.assign("SELECT SUM(c3) FROM rep1");
+ db.assign("TEST_DB");
+
+ if (verifySlave(sql,db) != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+/* TOOLS LIST
+
+ syncSlaveWithMaster()
+ {ensures slave is at same epoch as master}
+
+ verifySlaveLoad(BaseString *table)
+ {ensures slave table has same record count as master}
+
+ createTEST_DB()
+ {Creates TEST_DB database on master}
+
+ dropTEST_DB()
+ {Drops TEST_DB database on master}
+
+ verifySlave(BaseString& sql, BaseSting& db)
+ {The SQL statement must sum a column and will verify
+ that the sum of the column is equal on master & slave}
+*/
+
+
+NDBT_TESTSUITE(NdbRepStress);
+TESTCASE("PHASE_I_Stress","Basic Replication Stressing")
+{
+ INITIALIZER(createDB);
+ INITIALIZER(createTable_rep1);
+ STEP(stressNDB_rep1);
+ STEP(stressSQL_rep1);
+ FINALIZER(verifySlave_rep1);
+ FINALIZER(dropTEST_DB);
+}
+NDBT_TESTSUITE_END(NdbRepStress);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ NdbRepStress.setCreateAllTables(true);
+ return NdbRepStress.execute(argc, argv);
+}
+
diff --git a/storage/ndb/test/ndbapi/testBasic.cpp b/storage/ndb/test/ndbapi/testBasic.cpp
index 952b5a50dc5..ac23ceaad18 100644
--- a/storage/ndb/test/ndbapi/testBasic.cpp
+++ b/storage/ndb/test/ndbapi/testBasic.cpp
@@ -136,31 +136,13 @@ int runPkRead(NDBT_Context* ctx, NDBT_Step* step){
int loops = ctx->getNumLoops();
int records = ctx->getNumRecords();
int batchSize = ctx->getProperty("BatchSize", 1);
+ int lm = ctx->getProperty("LockMode", NdbOperation::LM_Read);
int i = 0;
HugoTransactions hugoTrans(*ctx->getTab());
while (i<loops) {
g_info << i << ": ";
- if (hugoTrans.pkReadRecords(GETNDB(step), records, batchSize) != NDBT_OK){
- g_info << endl;
- return NDBT_FAILED;
- }
- i++;
- }
- g_info << endl;
- return NDBT_OK;
-}
-
-int runPkDirtyRead(NDBT_Context* ctx, NDBT_Step* step){
- int loops = ctx->getNumLoops();
- int records = ctx->getNumRecords();
- int batchSize = ctx->getProperty("BatchSize", 1);
- int i = 0;
- bool dirty = true;
- HugoTransactions hugoTrans(*ctx->getTab());
- while (i<loops) {
- g_info << i << ": ";
- if (hugoTrans.pkReadRecords(GETNDB(step), records, batchSize,
- NdbOperation::LM_CommittedRead) != NDBT_OK){
+ if (hugoTrans.pkReadRecords(GETNDB(step), records, batchSize,
+ (NdbOperation::LockMode)lm) != NDBT_OK){
g_info << endl;
return NDBT_FAILED;
}
@@ -1552,14 +1534,23 @@ TESTCASE("PkInsert",
}
TESTCASE("PkRead",
"Verify that we can insert, read and delete from this table using PK"){
+ TC_PROPERTY("LockMode", NdbOperation::LM_Read);
INITIALIZER(runLoadTable);
STEP(runPkRead);
FINALIZER(runClearTable);
}
TESTCASE("PkDirtyRead",
"Verify that we can insert, dirty read and delete from this table using PK"){
+ TC_PROPERTY("LockMode", NdbOperation::LM_Dirty);
INITIALIZER(runLoadTable);
- STEP(runPkDirtyRead);
+ STEP(runPkRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkSimpleRead",
+ "Verify that we can insert, simple read and delete from this table using PK"){
+ TC_PROPERTY("LockMode", NdbOperation::LM_SimpleRead);
+ INITIALIZER(runLoadTable);
+ STEP(runPkRead);
FINALIZER(runClearTable);
}
TESTCASE("PkUpdate",
diff --git a/storage/ndb/test/ndbapi/testDict.cpp b/storage/ndb/test/ndbapi/testDict.cpp
index 22b795a8dba..ae940074067 100644
--- a/storage/ndb/test/ndbapi/testDict.cpp
+++ b/storage/ndb/test/ndbapi/testDict.cpp
@@ -684,7 +684,7 @@ int runTestFragmentTypes(NDBT_Context* ctx, NDBT_Step* step){
CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
CHECK(count == records);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
CHECK(count == (records/2));
@@ -862,7 +862,7 @@ int runPkSizes(NDBT_Context* ctx, NDBT_Step* step){
CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
CHECK(count == records);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
CHECK(count == (records/2));
CHECK(utilTrans.clearTable(pNdb, records) == 0);
@@ -2868,7 +2868,266 @@ runDictRestart(NDBT_Context* ctx, NDBT_Step* step)
return NDBT_OK;
}
+int
+runBug29501(NDBT_Context* ctx, NDBT_Step* step) {
+ NdbRestarter res;
+ NdbDictionary::LogfileGroup lg;
+ lg.setName("DEFAULT-LG");
+ lg.setUndoBufferSize(8*1024*1024);
+
+ if (res.getNumDbNodes() < 2)
+ return NDBT_OK;
+
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
+
+ int node = res.getRandomNotMasterNodeId(rand());
+ res.restartOneDbNode(node, true, true, false);
+
+ if(pDict->createLogfileGroup(lg) != 0){
+ g_err << "Failed to create logfilegroup:"
+ << endl << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ NdbDictionary::Undofile uf;
+ uf.setPath("undofile01.dat");
+ uf.setSize(5*1024*1024);
+ uf.setLogfileGroup("DEFAULT-LG");
+
+ if(pDict->createUndofile(uf) != 0){
+ g_err << "Failed to create undofile:"
+ << endl << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ res.waitNodesNoStart(&node, 1);
+ res.startNodes(&node, 1);
+
+ if (res.waitClusterStarted()){
+ g_err << "Node restart failed"
+ << endl << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ if (pDict->dropLogfileGroup(pDict->getLogfileGroup(lg.getName())) != 0){
+ g_err << "Drop of LFG Failed"
+ << endl << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int
+runDropDDObjects(NDBT_Context* ctx, NDBT_Step* step){
+ //Purpose is to drop all tables, data files, Table spaces and LFG's
+ Uint32 i = 0;
+
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
+
+ NdbDictionary::Dictionary::List list;
+ if (pDict->listObjects(list) == -1)
+ return NDBT_FAILED;
+
+ //Search the list and drop all tables found
+ const char * tableFound = 0;
+ for (i = 0; i < list.count; i++){
+ switch(list.elements[i].type){
+ case NdbDictionary::Object::UserTable:
+ tableFound = list.elements[i].name;
+ if(tableFound != 0){
+ if(strcmp(tableFound, "ndb_apply_status") != 0 &&
+ strcmp(tableFound, "NDB$BLOB_2_3") != 0 &&
+ strcmp(tableFound, "ndb_schema") != 0){
+ if(pDict->dropTable(tableFound) != 0){
+ g_err << "Failed to drop table: " << tableFound << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+ }
+ }
+ tableFound = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ //Search the list and drop all data file found
+ const char * dfFound = 0;
+ for (i = 0; i < list.count; i++){
+ switch(list.elements[i].type){
+ case NdbDictionary::Object::Datafile:
+ dfFound = list.elements[i].name;
+ if(dfFound != 0){
+ if(pDict->dropDatafile(pDict->getDatafile(0, dfFound)) != 0){
+ g_err << "Failed to drop datafile: " << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+ }
+ dfFound = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ //Search the list and drop all Table Spaces Found
+ const char * tsFound = 0;
+ for (i = 0; i <list.count; i++){
+ switch(list.elements[i].type){
+ case NdbDictionary::Object::Tablespace:
+ tsFound = list.elements[i].name;
+ if(tsFound != 0){
+ if(pDict->dropTablespace(pDict->getTablespace(tsFound)) != 0){
+ g_err << "Failed to drop tablespace: " << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+ }
+ tsFound = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ //Search the list and drop all LFG Found
+ //Currently only 1 LGF is supported, but written for future
+ //when more then one is supported.
+ const char * lgFound = 0;
+ for (i = 0; i < list.count; i++){
+ switch(list.elements[i].type){
+ case NdbDictionary::Object::LogfileGroup:
+ lgFound = list.elements[i].name;
+ if(lgFound != 0){
+ if (pDict->dropLogfileGroup(pDict->getLogfileGroup(lgFound)) != 0){
+ g_err << "Failed to drop tablespace: " << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+ }
+ lgFound = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int
+runWaitStarted(NDBT_Context* ctx, NDBT_Step* step){
+
+ NdbRestarter restarter;
+ restarter.waitClusterStarted(300);
+
+ NdbSleep_SecSleep(3);
+ return NDBT_OK;
+}
+
+int
+testDropDDObjectsSetup(NDBT_Context* ctx, NDBT_Step* step){
+ //Purpose is to setup to test DropDDObjects
+ char tsname[256];
+ char dfname[256];
+
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
+
+ NdbDictionary::LogfileGroup lg;
+ lg.setName("DEFAULT-LG");
+ lg.setUndoBufferSize(8*1024*1024);
+
+
+ if(pDict->createLogfileGroup(lg) != 0){
+ g_err << "Failed to create logfilegroup:"
+ << endl << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ NdbDictionary::Undofile uf;
+ uf.setPath("undofile01.dat");
+ uf.setSize(5*1024*1024);
+ uf.setLogfileGroup("DEFAULT-LG");
+
+ if(pDict->createUndofile(uf) != 0){
+ g_err << "Failed to create undofile:"
+ << endl << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ BaseString::snprintf(tsname, sizeof(tsname), "TS-%u", rand());
+ BaseString::snprintf(dfname, sizeof(dfname), "%s-%u.dat", tsname, rand());
+
+ if (create_tablespace(pDict, lg.getName(), tsname, dfname)){
+ g_err << "Failed to create undofile:"
+ << endl << pDict->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int
+DropDDObjectsVerify(NDBT_Context* ctx, NDBT_Step* step){
+ //Purpose is to verify test DropDDObjects worked
+ Uint32 i = 0;
+
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
+
+ NdbDictionary::Dictionary::List list;
+ if (pDict->listObjects(list) == -1)
+ return NDBT_FAILED;
+
+ bool ddFound = false;
+ for (i = 0; i <list.count; i++){
+ switch(list.elements[i].type){
+ case NdbDictionary::Object::Tablespace:
+ ddFound = true;
+ break;
+ case NdbDictionary::Object::LogfileGroup:
+ ddFound = true;
+ break;
+ default:
+ break;
+ }
+ if(ddFound == true){
+ g_err << "DropDDObjects Failed: DD found:"
+ << endl;
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
NDBT_TESTSUITE(testDict);
+TESTCASE("testDropDDObjects",
+ "* 1. start cluster\n"
+ "* 2. Create LFG\n"
+ "* 3. create TS\n"
+ "* 4. run DropDDObjects\n"
+ "* 5. Verify DropDDObjectsRestart worked\n"){
+INITIALIZER(runWaitStarted);
+INITIALIZER(runDropDDObjects);
+INITIALIZER(testDropDDObjectsSetup);
+STEP(runDropDDObjects);
+FINALIZER(DropDDObjectsVerify);
+}
+
+TESTCASE("Bug29501",
+ "* 1. start cluster\n"
+ "* 2. Restart 1 node -abort -nostart\n"
+ "* 3. create LFG\n"
+ "* 4. Restart data node\n"
+ "* 5. Restart 1 node -nostart\n"
+ "* 6. Drop LFG\n"){
+INITIALIZER(runWaitStarted);
+INITIALIZER(runDropDDObjects);
+STEP(runBug29501);
+FINALIZER(runDropDDObjects);
+}
TESTCASE("CreateAndDrop",
"Try to create and drop the table loop number of times\n"){
INITIALIZER(runCreateAndDrop);
diff --git a/storage/ndb/test/ndbapi/testIndex.cpp b/storage/ndb/test/ndbapi/testIndex.cpp
index 00e559c7a0f..bd9ff7ac607 100644
--- a/storage/ndb/test/ndbapi/testIndex.cpp
+++ b/storage/ndb/test/ndbapi/testIndex.cpp
@@ -809,7 +809,7 @@ int runSystemRestart1(NDBT_Context* ctx, NDBT_Step* step){
CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
@@ -834,7 +834,7 @@ int runSystemRestart1(NDBT_Context* ctx, NDBT_Step* step){
CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
diff --git a/storage/ndb/test/ndbapi/testNodeRestart.cpp b/storage/ndb/test/ndbapi/testNodeRestart.cpp
index 99b72699762..751134c43c5 100644
--- a/storage/ndb/test/ndbapi/testNodeRestart.cpp
+++ b/storage/ndb/test/ndbapi/testNodeRestart.cpp
@@ -1590,6 +1590,8 @@ runBug27466(NDBT_Context* ctx, NDBT_Step* step)
node2 = res.getDbNodeId(rand() % res.getNumDbNodes());
}
+ ndbout_c("nodes %u %u", node1, node2);
+
if (res.restartOneDbNode(node1, false, true, true))
return NDBT_FAILED;
@@ -1762,6 +1764,125 @@ runBug28717(NDBT_Context* ctx, NDBT_Step* step)
return NDBT_OK;
}
+int
+runBug31525(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ NdbRestarter res;
+
+ if (res.getNumDbNodes() < 2)
+ {
+ return NDBT_OK;
+ }
+
+ int nodes[2];
+ nodes[0] = res.getMasterNodeId();
+ nodes[1] = res.getNextMasterNodeId(nodes[0]);
+
+ while (res.getNodeGroup(nodes[0]) != res.getNodeGroup(nodes[1]))
+ {
+ ndbout_c("Restarting %u as it not in same node group as %u",
+ nodes[1], nodes[0]);
+ if (res.restartOneDbNode(nodes[1], false, true, true))
+ return NDBT_FAILED;
+
+ if (res.waitNodesNoStart(nodes+1, 1))
+ return NDBT_FAILED;
+
+ if (res.startNodes(nodes+1, 1))
+ return NDBT_FAILED;
+
+ if (res.waitClusterStarted())
+ return NDBT_FAILED;
+
+ nodes[1] = res.getNextMasterNodeId(nodes[0]);
+ }
+
+ ndbout_c("nodes[0]: %u nodes[1]: %u", nodes[0], nodes[1]);
+
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if (res.dumpStateAllNodes(&val, 1))
+ return NDBT_FAILED;
+
+ int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+ if (res.dumpStateAllNodes(val2, 2))
+ return NDBT_FAILED;
+
+ if (res.insertErrorInAllNodes(932))
+ return NDBT_FAILED;
+
+ if (res.insertErrorInNode(nodes[1], 7192))
+ return NDBT_FAILED;
+
+ if (res.insertErrorInNode(nodes[0], 7191))
+ return NDBT_FAILED;
+
+ if (res.waitClusterNoStart())
+ return NDBT_FAILED;
+
+ if (res.startAll())
+ return NDBT_FAILED;
+
+ if (res.waitClusterStarted())
+ return NDBT_FAILED;
+
+ if (res.restartOneDbNode(nodes[1], false, false, true))
+ return NDBT_FAILED;
+
+ if (res.waitClusterStarted())
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
+int
+runBug32160(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ NdbRestarter res;
+
+ if (res.getNumDbNodes() < 2)
+ {
+ return NDBT_OK;
+ }
+
+ int master = res.getMasterNodeId();
+ int next = res.getNextMasterNodeId(master);
+
+ if (res.insertErrorInNode(next, 7194))
+ {
+ return NDBT_FAILED;
+ }
+
+ int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+ if (res.dumpStateOneNode(master, val2, 2))
+ return NDBT_FAILED;
+
+ if (res.insertErrorInNode(master, 7193))
+ return NDBT_FAILED;
+
+ int val3[] = { 7099 };
+ if (res.dumpStateOneNode(master, val3, 1))
+ return NDBT_FAILED;
+
+ if (res.waitNodesNoStart(&master, 1))
+ return NDBT_FAILED;
+
+ if (res.startNodes(&master, 1))
+ return NDBT_FAILED;
+
+ if (res.waitClusterStarted())
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
NDBT_TESTSUITE(testNodeRestart);
TESTCASE("NoLoad",
"Test that one node at a time can be stopped and then restarted "\
@@ -2085,6 +2206,9 @@ TESTCASE("Bug21271",
STEP(runPkUpdateUntilStopped);
FINALIZER(runClearTable);
}
+TESTCASE("Bug31525", ""){
+ INITIALIZER(runBug31525);
+}
TESTCASE("Bug24717", ""){
INITIALIZER(runBug24717);
}
@@ -2128,6 +2252,9 @@ TESTCASE("Bug28717", ""){
TESTCASE("Bug29364", ""){
INITIALIZER(runBug29364);
}
+TESTCASE("Bug32160", ""){
+ INITIALIZER(runBug32160);
+}
NDBT_TESTSUITE_END(testNodeRestart);
int main(int argc, const char** argv){
diff --git a/storage/ndb/test/ndbapi/testScan.cpp b/storage/ndb/test/ndbapi/testScan.cpp
index 2561869fa5f..df6dbe2e550 100644
--- a/storage/ndb/test/ndbapi/testScan.cpp
+++ b/storage/ndb/test/ndbapi/testScan.cpp
@@ -579,7 +579,7 @@ int runScanUpdateUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
para = myRandom48(239)+1;
g_info << i << ": ";
- if (hugoTrans.scanUpdateRecords(GETNDB(step), records, 0, para) == NDBT_FAILED){
+ if (hugoTrans.scanUpdateRecords(GETNDB(step), 0, 0, para) == NDBT_FAILED){
return NDBT_FAILED;
}
i++;
diff --git a/storage/ndb/test/ndbapi/testSystemRestart.cpp b/storage/ndb/test/ndbapi/testSystemRestart.cpp
index 901c0e35568..0f9100f67fa 100644
--- a/storage/ndb/test/ndbapi/testSystemRestart.cpp
+++ b/storage/ndb/test/ndbapi/testSystemRestart.cpp
@@ -20,6 +20,7 @@
#include <NdbRestarter.hpp>
#include <Vector.hpp>
#include <signaldata/DumpStateOrd.hpp>
+#include <NdbBackup.hpp>
int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
@@ -121,7 +122,7 @@ int runSystemRestart1(NDBT_Context* ctx, NDBT_Step* step){
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
@@ -142,7 +143,7 @@ int runSystemRestart1(NDBT_Context* ctx, NDBT_Step* step){
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
@@ -265,7 +266,7 @@ int runSystemRestart2(NDBT_Context* ctx, NDBT_Step* step){
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
@@ -329,7 +330,7 @@ int runSystemRestartTestUndoLog(NDBT_Context* ctx, NDBT_Step* step){
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
- CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
CHECK(hugoTrans.loadTable(pNdb, records) == 0);
CHECK(utilTrans.clearTable(pNdb, records) == 0);
@@ -1293,6 +1294,292 @@ runBug28770(NDBT_Context* ctx, NDBT_Step* step) {
return result;
}
+int
+runStopper(NDBT_Context* ctx, NDBT_Step* step)
+{
+ NdbRestarter restarter;
+ Uint32 stop = 0;
+loop:
+ while (!ctx->isTestStopped() &&
+ ((stop = ctx->getProperty("StopAbort", Uint32(0))) == 0))
+ {
+ NdbSleep_MilliSleep(30);
+ }
+
+ if (ctx->isTestStopped())
+ {
+ return NDBT_OK;
+ }
+
+ ndbout << "Killing in " << stop << "ms..." << flush;
+ NdbSleep_MilliSleep(stop);
+ restarter.restartAll(false, true, true);
+ ctx->setProperty("StopAbort", Uint32(0));
+ goto loop;
+}
+
+int runSR_DD_1(NDBT_Context* ctx, NDBT_Step* step)
+{
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ Uint32 loops = ctx->getNumLoops();
+ int count;
+ NdbRestarter restarter;
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+ bool lcploop = ctx->getProperty("LCP", (unsigned)0);
+ bool all = ctx->getProperty("ALL", (unsigned)0);
+
+ Uint32 i = 1;
+ Uint32 backupId;
+
+ int val[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+ int lcp = DumpStateOrd::DihMinTimeBetweenLCP;
+
+ int startFrom = 0;
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while(i<=loops && result != NDBT_FAILED)
+ {
+
+ if (lcploop)
+ {
+ CHECK(restarter.dumpStateAllNodes(&lcp, 1) == 0);
+ }
+
+ int nodeId = restarter.getDbNodeId(rand() % restarter.getNumDbNodes());
+ //CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+
+ ndbout << "Loop " << i << "/"<< loops <<" started" << endl;
+ ndbout << "Loading records..." << startFrom << endl;
+ CHECK(hugoTrans.loadTable(pNdb, startFrom) == 0);
+
+ if (!all)
+ {
+ ndbout << "Making " << nodeId << " crash" << endl;
+ int kill[] = { 9999, 1000, 3000 };
+ CHECK(restarter.dumpStateOneNode(nodeId, val, 2) == 0);
+ CHECK(restarter.dumpStateOneNode(nodeId, kill, 3) == 0);
+ }
+ else
+ {
+ ndbout << "Crashing cluster" << endl;
+ ctx->setProperty("StopAbort", 1000 + rand() % (3000 - 1000));
+ }
+ Uint64 end = NdbTick_CurrentMillisecond() + 4000;
+ Uint32 row = startFrom;
+ do {
+ ndbout << "Loading from " << row << " to " << row + 1000 << endl;
+ if (hugoTrans.loadTableStartFrom(pNdb, row, 1000) != 0)
+ break;
+ row += 1000;
+ } while (NdbTick_CurrentMillisecond() < end);
+
+ if (!all)
+ {
+ ndbout << "Waiting for " << nodeId << " to restart" << endl;
+ CHECK(restarter.waitNodesNoStart(&nodeId, 1) == 0);
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll(false, true, true) == 0);
+ }
+ else
+ {
+ ndbout << "Waiting for cluster to restart" << endl;
+ }
+ CHECK(restarter.waitClusterNoStart() == 0);
+ CHECK(restarter.startAll() == 0);
+ CHECK(restarter.waitClusterStarted() == 0);
+
+ ndbout << "Starting backup..." << flush;
+ CHECK(backup.start(backupId) == 0);
+ ndbout << "done" << endl;
+
+ int cnt = 0;
+ CHECK(hugoTrans.selectCount(pNdb, 0, &cnt) == 0);
+ ndbout << "Found " << cnt << " records..." << endl;
+ ndbout << "Clearing..." << endl;
+ CHECK(hugoTrans.clearTable(pNdb,
+ NdbScanOperation::SF_TupScan, cnt) == 0);
+
+ if (cnt > startFrom)
+ {
+ startFrom = cnt;
+ }
+ startFrom += 1000;
+ i++;
+ }
+
+ ndbout << "runSR_DD_1 finished" << endl;
+ ctx->stopTest();
+ return result;
+}
+
+int runSR_DD_2(NDBT_Context* ctx, NDBT_Step* step)
+{
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ Uint32 loops = ctx->getNumLoops();
+ Uint32 rows = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+ bool lcploop = ctx->getProperty("LCP", (unsigned)0);
+ bool all = ctx->getProperty("ALL", (unsigned)0);
+
+ Uint32 i = 1;
+ Uint32 backupId;
+
+ int val[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+ int lcp = DumpStateOrd::DihMinTimeBetweenLCP;
+
+ int startFrom = 0;
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while(i<=loops && result != NDBT_FAILED)
+ {
+
+ if (lcploop)
+ {
+ CHECK(restarter.dumpStateAllNodes(&lcp, 1) == 0);
+ }
+
+ int nodeId = restarter.getDbNodeId(rand() % restarter.getNumDbNodes());
+
+ if (!all)
+ {
+ ndbout << "Making " << nodeId << " crash" << endl;
+ int kill[] = { 9999, 3000, 10000 };
+ CHECK(restarter.dumpStateOneNode(nodeId, val, 2) == 0);
+ CHECK(restarter.dumpStateOneNode(nodeId, kill, 3) == 0);
+ }
+ else
+ {
+ ndbout << "Crashing cluster" << endl;
+ ctx->setProperty("StopAbort", 1000 + rand() % (3000 - 1000));
+ }
+
+ Uint64 end = NdbTick_CurrentMillisecond() + 11000;
+ Uint32 row = startFrom;
+ do {
+ if (hugoTrans.loadTable(pNdb, rows) != 0)
+ break;
+
+ if (hugoTrans.clearTable(pNdb, NdbScanOperation::SF_TupScan, rows) != 0)
+ break;
+ } while (NdbTick_CurrentMillisecond() < end);
+
+ if (!all)
+ {
+ ndbout << "Waiting for " << nodeId << " to restart" << endl;
+ CHECK(restarter.waitNodesNoStart(&nodeId, 1) == 0);
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll(false, true, true) == 0);
+ }
+ else
+ {
+ ndbout << "Waiting for cluster to restart" << endl;
+ }
+
+ CHECK(restarter.waitClusterNoStart() == 0);
+ CHECK(restarter.startAll() == 0);
+ CHECK(restarter.waitClusterStarted() == 0);
+
+ ndbout << "Starting backup..." << flush;
+ CHECK(backup.start(backupId) == 0);
+ ndbout << "done" << endl;
+
+ int cnt = 0;
+ CHECK(hugoTrans.selectCount(pNdb, 0, &cnt) == 0);
+ ndbout << "Found " << cnt << " records..." << endl;
+ ndbout << "Clearing..." << endl;
+ CHECK(hugoTrans.clearTable(pNdb,
+ NdbScanOperation::SF_TupScan, cnt) == 0);
+ i++;
+ }
+
+ ndbout << "runSR_DD_2 finished" << endl;
+ ctx->stopTest();
+ return result;
+}
+
+int runBug22696(NDBT_Context* ctx, NDBT_Step* step)
+{
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ Uint32 loops = ctx->getNumLoops();
+ Uint32 rows = ctx->getNumRecords();
+ NdbRestarter restarter;
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ Uint32 i = 0;
+ while(i<=loops && result != NDBT_FAILED)
+ {
+ for (Uint32 j = 0; j<10 && result != NDBT_FAILED; j++)
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, rows) == 0);
+
+ CHECK(restarter.restartAll(false, true, i > 0 ? true : false) == 0);
+ CHECK(restarter.waitClusterNoStart() == 0);
+ CHECK(restarter.insertErrorInAllNodes(7072) == 0);
+ CHECK(restarter.startAll() == 0);
+ CHECK(restarter.waitClusterStarted() == 0);
+
+ i++;
+ if (i < loops)
+ {
+ NdbSleep_SecSleep(5); // Wait for a few gcp
+ }
+ }
+
+ ctx->stopTest();
+ return result;
+}
+
+int
+runBug27434(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int result = NDBT_OK;
+ NdbRestarter restarter;
+ Ndb* pNdb = GETNDB(step);
+ const Uint32 nodeCount = restarter.getNumDbNodes();
+
+ if (nodeCount < 2)
+ return NDBT_OK;
+
+ int args[] = { DumpStateOrd::DihMaxTimeBetweenLCP };
+ int dump[] = { DumpStateOrd::DihStartLcpImmediately };
+
+ int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_CHECKPOINT, 0 };
+ NdbLogEventHandle handle =
+ ndb_mgm_create_logevent_handle(restarter.handle, filter);
+
+ struct ndb_logevent event;
+
+ do {
+ int node1 = restarter.getDbNodeId(rand() % nodeCount);
+ CHECK(restarter.restartOneDbNode(node1, false, true, true) == 0);
+ NdbSleep_SecSleep(3);
+ CHECK(restarter.waitNodesNoStart(&node1, 1) == 0);
+
+ CHECK(restarter.dumpStateAllNodes(args, 1) == 0);
+
+ for (Uint32 i = 0; i<3; i++)
+ {
+ CHECK(restarter.dumpStateAllNodes(dump, 1) == 0);
+ while(ndb_logevent_get_next(handle, &event, 0) >= 0 &&
+ event.type != NDB_LE_LocalCheckpointStarted);
+ while(ndb_logevent_get_next(handle, &event, 0) >= 0 &&
+ event.type != NDB_LE_LocalCheckpointCompleted);
+ }
+
+ restarter.restartAll(false, true, true);
+ NdbSleep_SecSleep(3);
+ CHECK(restarter.waitClusterNoStart() == 0);
+ restarter.insertErrorInNode(node1, 5046);
+ restarter.startAll();
+ CHECK(restarter.waitClusterStarted() == 0);
+ } while(false);
+
+ return result;
+}
NDBT_TESTSUITE(testSystemRestart);
TESTCASE("SR1",
@@ -1474,6 +1761,72 @@ TESTCASE("Bug24664",
STEP(runBug24664);
FINALIZER(runClearTable);
}
+TESTCASE("Bug27434",
+ "")
+{
+ INITIALIZER(runWaitStarted);
+ STEP(runBug27434);
+}
+TESTCASE("SR_DD_1", "")
+{
+ TC_PROPERTY("ALL", 1);
+ INITIALIZER(runWaitStarted);
+ STEP(runStopper);
+ STEP(runSR_DD_1);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_DD_1b", "")
+{
+ INITIALIZER(runWaitStarted);
+ STEP(runSR_DD_1);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_DD_1_LCP", "")
+{
+ TC_PROPERTY("ALL", 1);
+ TC_PROPERTY("LCP", 1);
+ INITIALIZER(runWaitStarted);
+ STEP(runStopper);
+ STEP(runSR_DD_1);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_DD_1b_LCP", "")
+{
+ TC_PROPERTY("LCP", 1);
+ INITIALIZER(runWaitStarted);
+ STEP(runSR_DD_1);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_DD_2", "")
+{
+ TC_PROPERTY("ALL", 1);
+ INITIALIZER(runWaitStarted);
+ STEP(runStopper);
+ STEP(runSR_DD_2);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_DD_2b", "")
+{
+ INITIALIZER(runWaitStarted);
+ STEP(runSR_DD_2);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_DD_2_LCP", "")
+{
+ TC_PROPERTY("ALL", 1);
+ TC_PROPERTY("LCP", 1);
+ INITIALIZER(runWaitStarted);
+ STEP(runStopper);
+ STEP(runSR_DD_2);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_DD_2b_LCP", "")
+{
+ TC_PROPERTY("LCP", 1);
+ INITIALIZER(runWaitStarted);
+ STEP(runSR_DD_2);
+ FINALIZER(runClearTable);
+}
TESTCASE("Bug29167", "")
{
INITIALIZER(runWaitStarted);
@@ -1492,8 +1845,13 @@ TESTCASE("Bug28770",
STEP(runBug28770);
FINALIZER(runClearTable);
}
-
-
+TESTCASE("Bug22696", "")
+{
+ INITIALIZER(runWaitStarted);
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runBug22696);
+ FINALIZER(runClearTable);
+}
NDBT_TESTSUITE_END(testSystemRestart);
int main(int argc, const char** argv){
diff --git a/storage/ndb/test/ndbapi/test_event.cpp b/storage/ndb/test/ndbapi/test_event.cpp
index e1e0012d0d8..18825d734a4 100644
--- a/storage/ndb/test/ndbapi/test_event.cpp
+++ b/storage/ndb/test/ndbapi/test_event.cpp
@@ -21,6 +21,7 @@
#include <NdbAutoPtr.hpp>
#include <NdbRestarter.hpp>
#include <NdbRestarts.hpp>
+#include <signaldata/DumpStateOrd.hpp>
#define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb()
@@ -1730,7 +1731,7 @@ runScanUpdateUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
HugoTransactions hugoTrans(*ctx->getTab());
while (ctx->isTestStopped() == false)
{
- if (hugoTrans.scanUpdateRecords(GETNDB(step), records, abort,
+ if (hugoTrans.scanUpdateRecords(GETNDB(step), 0, abort,
parallelism) == NDBT_FAILED){
return NDBT_FAILED;
}
@@ -1758,6 +1759,85 @@ runInsertDeleteUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
return NDBT_OK;
}
+int
+runBug31701(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int result = NDBT_OK;
+
+ NdbRestarter restarter;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+ // This should really wait for applier to start...10s is likely enough
+ NdbSleep_SecSleep(10);
+
+ int nodeId = restarter.getDbNodeId(rand() % restarter.getNumDbNodes());
+
+ int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+ if (restarter.dumpStateOneNode(nodeId, val2, 2))
+ return NDBT_FAILED;
+
+ restarter.insertErrorInNode(nodeId, 13033);
+ if (restarter.waitNodesNoStart(&nodeId, 1))
+ return NDBT_FAILED;
+
+ if (restarter.startNodes(&nodeId, 1))
+ return NDBT_FAILED;
+
+ if (restarter.waitClusterStarted())
+ return NDBT_FAILED;
+
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ if(ctx->getPropertyWait("LastGCI", ~(Uint32)0))
+ {
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+
+ hugoTrans.clearTable(GETNDB(step), 0);
+
+ if (hugoTrans.loadTable(GETNDB(step), 3*records, 1, true, 1) != 0){
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+
+ if (hugoTrans.pkDelRecords(GETNDB(step), 3*records, 1, true, 1) != 0){
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.loadTable(GETNDB(step), records, 1, true, 1) != 0){
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, 1, 1) != 0){
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, 1, 1) != 0){
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, 1, 1) != 0){
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->setProperty("LastGCI", hugoTrans.m_latest_gci);
+ if(ctx->getPropertyWait("LastGCI", ~(Uint32)0))
+ {
+ g_err << "FAIL " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->stopTest();
+ return NDBT_OK;
+}
+
NDBT_TESTSUITE(test_event);
TESTCASE("BasicEventOperation",
"Verify that we can listen to Events"
@@ -1887,6 +1967,14 @@ TESTCASE("Bug27169", ""){
STEP(runRestarterLoop);
FINALIZER(runDropEvent);
}
+TESTCASE("Bug31701", ""){
+ INITIALIZER(runCreateEvent);
+ INITIALIZER(runCreateShadowTable);
+ STEP(runEventApplier);
+ STEP(runBug31701);
+ FINALIZER(runDropEvent);
+ FINALIZER(runDropShadowTable);
+}
NDBT_TESTSUITE_END(test_event);
int main(int argc, const char** argv){
diff --git a/storage/ndb/test/run-test/daily-basic-tests.txt b/storage/ndb/test/run-test/daily-basic-tests.txt
index b7a3a15dae7..37db5e01dd6 100644
--- a/storage/ndb/test/run-test/daily-basic-tests.txt
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt
@@ -65,6 +65,14 @@ args: -n PkRead
max-time: 500
cmd: testBasic
+args: -n PkSimpleRead
+
+max-time: 500
+cmd: testBasic
+args: -n PkDirtyRead
+
+max-time: 500
+cmd: testBasic
args: -n PkUpdate
max-time: 500
@@ -555,7 +563,7 @@ args: -n Bug25554 T1
max-time: 3000
cmd: testNodeRestart
-args: -n Bug25984
+args: -n Bug25984 T1
max-time: 1000
cmd: testNodeRestart
@@ -573,8 +581,20 @@ max-time: 1000
cmd: testNodeRestart
args: -n Bug29364 T1
+max-time: 300
+cmd: testNodeRestart
+args: -n Bug32160 T1
+
#
# DICT TESTS
+max-time: 500
+cmd: testDict
+args: -n Bug29501 T1
+
+max-time: 500
+cmd: testDict
+args: -n testDropDDObjects T1
+
max-time: 1500
cmd: testDict
args: -n CreateAndDrop
@@ -864,6 +884,10 @@ max-time: 1000
cmd: testNodeRestart
args: -n Bug27466 T1
+max-time: 1500
+cmd: testSystemRestart
+args: -n Bug27434 T1
+
max-time: 1000
cmd: test_event
args: -l 10 -n Bug27169 T1
@@ -945,3 +969,80 @@ args: -n Bug28804 T1 T3
max-time: 180
cmd: testIndex
args: -n Bug28804_ATTRINFO T1 T3
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1 D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1b D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1 D2
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1b D2
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1_LCP D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1b_LCP D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1_LCP D2
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_1b_LCP D2
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2 D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2b D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2 D2
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2b D2
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2_LCP D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2b_LCP D1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2_LCP D2
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_DD_2b_LCP D2
+
+max-time: 600
+cmd: testNodeRestart
+args: -n Bug31525 T1
+
+max-time: 300
+cmd: test_event
+args: -n Bug31701 T1
+
+max-time: 300
+cmd: testSystemRestart
+args: -n Bug22696 T1
+
diff --git a/storage/ndb/test/src/DbUtil.cpp b/storage/ndb/test/src/DbUtil.cpp
new file mode 100755
index 00000000000..1df4f8f745e
--- /dev/null
+++ b/storage/ndb/test/src/DbUtil.cpp
@@ -0,0 +1,337 @@
+/* Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* DbUtil.cpp: implementation of the database utilities class.*/
+
+#include "DbUtil.hpp"
+
+/* Constructors */
+
+DbUtil::DbUtil(const char * dbname)
+{
+ m_port = 0;
+ m_connected = false;
+ this->setDbName(dbname);
+}
+
+DbUtil::DbUtil(const char * dbname, const char* suffix)
+{
+ this->setDbName(dbname);
+ m_connected = false;
+
+ const char* env= getenv("MYSQL_HOME");
+ if (env && strlen(env))
+ {
+ default_file.assfmt("%s/my.cnf", env);
+ }
+
+ if (suffix != NULL){
+ default_group.assfmt("client%s", suffix);
+ }
+ else {
+ default_group.assign("client.1.master");
+ }
+
+ ndbout << "default_file: " << default_file.c_str() << endl;
+ ndbout << "default_group: " << default_group.c_str() << endl;
+}
+
+/* Destructor*/
+
+DbUtil::~DbUtil()
+{
+ this->databaseLogout();
+}
+
+/* Database Login */
+
+void
+DbUtil::databaseLogin(const char* system, const char* usr,
+ const char* password, unsigned int portIn,
+ const char* sockIn, bool transactional)
+{
+ if (!(mysql = mysql_init(NULL)))
+ {
+ myerror("DB Login-> mysql_init() failed");
+ exit(DBU_FAILED);
+ }
+ this->setUser(usr);
+ this->setHost(system);
+ this->setPassword(password);
+ this->setPort(portIn);
+ this->setSocket(sockIn);
+
+ if (!(mysql_real_connect(mysql,
+ m_host.c_str(),
+ m_user.c_str(),
+ m_pass.c_str(),
+ "test",
+ m_port,
+ m_socket.c_str(), 0)))
+ {
+ myerror("connection failed");
+ mysql_close(mysql);
+ exit(DBU_FAILED);
+ }
+
+ mysql->reconnect = DBU_TRUE;
+
+ /* set AUTOCOMMIT */
+ if(!transactional)
+ mysql_autocommit(mysql, DBU_TRUE);
+ else
+ mysql_autocommit(mysql, DBU_FALSE);
+
+ #ifdef DEBUG
+ printf("\n\tConnected to MySQL server version: %s (%lu)\n\n",
+ mysql_get_server_info(mysql),
+ (unsigned long) mysql_get_server_version(mysql));
+ #endif
+ this->selectDb();
+}
+
+/* Database Connect */
+
+int
+DbUtil::connect()
+{
+ if (!(mysql = mysql_init(NULL)))
+ {
+ myerror("DB connect-> mysql_init() failed");
+ return DBU_FAILED;
+ }
+
+ /* Load connection parameters file and group */
+ if (mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, default_file.c_str()) ||
+ mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, default_group.c_str()))
+ {
+ myerror("DB Connect -> mysql_options failed");
+ return DBU_FAILED;
+ }
+
+ /*
+ Connect, read settings from my.cnf
+ NOTE! user and password can be stored there as well
+ */
+
+ if (mysql_real_connect(mysql, NULL, "root","", m_dbname.c_str(),
+ 0, NULL, 0) == NULL)
+ {
+ myerror("connection failed");
+ mysql_close(mysql);
+ return DBU_FAILED;
+ }
+ this->selectDb();
+ m_connected = true;
+ return DBU_OK;
+}
+
+
+/* Database Logout */
+
+void
+DbUtil::databaseLogout()
+{
+ if (mysql){
+ #ifdef DEBUG
+ printf("\n\tClosing the MySQL database connection ...\n\n");
+ #endif
+ mysql_close(mysql);
+ }
+}
+
+/* Prepare MySQL Statements Cont */
+
+MYSQL_STMT *STDCALL
+DbUtil::mysqlSimplePrepare(const char *query)
+{
+ #ifdef DEBUG
+ printf("Inside DbUtil::mysqlSimplePrepare\n");
+ #endif
+ int m_res = DBU_OK;
+
+ MYSQL_STMT *my_stmt= mysql_stmt_init(this->getMysql());
+ if (my_stmt && (m_res = mysql_stmt_prepare(my_stmt, query, strlen(query)))){
+ this->printStError(my_stmt,"Prepare Statement Failed");
+ mysql_stmt_close(my_stmt);
+ exit(DBU_FAILED);
+ }
+ return my_stmt;
+}
+
+/* Close MySQL Statements Handle */
+
+void
+DbUtil::mysqlCloseStmHandle(MYSQL_STMT *my_stmt)
+{
+ mysql_stmt_close(my_stmt);
+}
+
+/* Error Printing */
+
+void
+DbUtil::printError(const char *msg)
+{
+ if (this->getMysql() && mysql_errno(this->getMysql()))
+ {
+ if (this->getMysql()->server_version)
+ printf("\n [MySQL-%s]", this->getMysql()->server_version);
+ else
+ printf("\n [MySQL]");
+ printf("[%d] %s\n", this->getErrorNumber(), this->getError());
+ }
+ else if (msg)
+ printf(" [MySQL] %s\n", msg);
+}
+
+void
+DbUtil::printStError(MYSQL_STMT *stmt, const char *msg)
+{
+ if (stmt && mysql_stmt_errno(stmt))
+ {
+ if (this->getMysql() && this->getMysql()->server_version)
+ printf("\n [MySQL-%s]", this->getMysql()->server_version);
+ else
+ printf("\n [MySQL]");
+
+ printf("[%d] %s\n", mysql_stmt_errno(stmt),
+ mysql_stmt_error(stmt));
+ }
+ else if (msg)
+ printf("[MySQL] %s\n", msg);
+}
+
+/* Select which database to use */
+
+int
+DbUtil::selectDb()
+{
+ if ((this->getDbName()) != NULL)
+ {
+ if(mysql_select_db(this->getMysql(), this->getDbName()))
+ {
+ this->printError("mysql_select_db failed");
+ return DBU_FAILED;
+ }
+ return DBU_OK;
+ }
+ this->printError("getDbName() == NULL");
+ return DBU_FAILED;
+}
+
+int
+DbUtil::selectDb(const char * m_db)
+{
+ {
+ if(mysql_select_db(this->getMysql(), m_db))
+ {
+ this->printError("mysql_select_db failed");
+ return DBU_FAILED;
+ }
+ return DBU_OK;
+ }
+}
+
+int
+DbUtil::createDb(BaseString& m_db)
+{
+ BaseString stm;
+ {
+ if(mysql_select_db(this->getMysql(), m_db.c_str()) == DBU_OK)
+ {
+ stm.assfmt("DROP DATABASE %s", m_db.c_str());
+ if(this->doQuery(m_db.c_str()) == DBU_FAILED)
+ return DBU_FAILED;
+ }
+ stm.assfmt("CREATE DATABASE %s", m_db.c_str());
+ if(this->doQuery(m_db.c_str()) == DBU_FAILED)
+ return DBU_FAILED;
+ return DBU_OK;
+ }
+}
+
+/* Run Simple Queries */
+
+int
+DbUtil::doQuery(BaseString& str)
+{
+ if(mysql_query(this->getMysql(), str.c_str()))
+ {
+ this->printError(str.c_str());
+ return DBU_FAILED;
+ }
+ return DBU_OK;
+}
+
+int
+DbUtil::doQuery(const char * stm)
+{
+ if(mysql_query(this->getMysql(), stm))
+ {
+ this->printError(stm);
+ return DBU_FAILED;
+ }
+ return DBU_OK;
+}
+
+/* Return MySQL Error String */
+
+const char *
+DbUtil::getError()
+{
+ return mysql_error(this->getMysql());
+}
+
+/* Retrun MySQL Error Number */
+
+int
+DbUtil::getErrorNumber()
+{
+ return mysql_errno(this->getMysql());
+}
+
+/* Count Table Rows */
+
+unsigned long
+DbUtil::selectCountTable(const char * table)
+{
+ unsigned long m_count = 0;
+ BaseString m_query;
+
+ m_query.assfmt("select count(*) from %s", table);
+ if (mysql_query(this->getMysql(),m_query.c_str()) ||
+ !(m_result=mysql_store_result(this->getMysql())))
+ {
+ this->printError("selectCountTable\n");
+ return DBU_FAILED;
+ }
+ m_row = mysql_fetch_row(m_result);
+ m_count = (ulong) strtoull(m_row[0], (char**) 0, 10);
+ mysql_free_result(m_result);
+
+ return m_count;
+}
+
+/* DIE */
+
+void
+DbUtil::die(const char *file, int line, const char *expr)
+{
+ printf("%s:%d: check failed: '%s'\n", file, line, expr);
+ abort();
+}
+
+/* EOF */
+
diff --git a/storage/ndb/test/src/HugoOperations.cpp b/storage/ndb/test/src/HugoOperations.cpp
index 1a2e5180f1f..93a9eaf435a 100644
--- a/storage/ndb/test/src/HugoOperations.cpp
+++ b/storage/ndb/test/src/HugoOperations.cpp
@@ -93,6 +93,7 @@ rand_lock_mode:
case NdbOperation::LM_Read:
case NdbOperation::LM_Exclusive:
case NdbOperation::LM_CommittedRead:
+ case NdbOperation::LM_SimpleRead:
if(idx && idx->getType() == NdbDictionary::Index::OrderedIndex &&
pIndexScanOp == 0)
{
diff --git a/storage/ndb/test/src/HugoTransactions.cpp b/storage/ndb/test/src/HugoTransactions.cpp
index 3a1600815e0..0e5f7cd8115 100644
--- a/storage/ndb/test/src/HugoTransactions.cpp
+++ b/storage/ndb/test/src/HugoTransactions.cpp
@@ -341,50 +341,14 @@ HugoTransactions::scanReadRecords(Ndb* pNdb,
int
HugoTransactions::scanUpdateRecords(Ndb* pNdb,
- int records,
- int abortPercent,
- int parallelism){
- if(m_defaultScanUpdateMethod == 1){
- return scanUpdateRecords1(pNdb, records, abortPercent, parallelism);
- } else if(m_defaultScanUpdateMethod == 2){
- return scanUpdateRecords2(pNdb, records, abortPercent, parallelism);
- } else {
- return scanUpdateRecords3(pNdb, records, abortPercent, parallelism);
- }
-}
-
-// Scan all records exclusive and update
-// them one by one
-int
-HugoTransactions::scanUpdateRecords1(Ndb* pNdb,
- int records,
- int abortPercent,
- int parallelism){
- return scanUpdateRecords3(pNdb, records, abortPercent, 1);
-}
-
-// Scan all records exclusive and update
-// them batched by asking nextScanResult to
-// give us all cached records before fetching new
-// records from db
-int
-HugoTransactions::scanUpdateRecords2(Ndb* pNdb,
- int records,
- int abortPercent,
- int parallelism){
- return scanUpdateRecords3(pNdb, records, abortPercent, parallelism);
-}
-
-int
-HugoTransactions::scanUpdateRecords3(Ndb* pNdb,
- int records,
- int abortPercent,
- int parallelism){
- int retryAttempt = 0;
+ NdbScanOperation::ScanFlag flags,
+ int records,
+ int abortPercent,
+ int parallelism){
+ int retryAttempt = 0;
int check, a;
NdbScanOperation *pOp;
-
while (true){
restart:
if (retryAttempt++ >= m_retryMax){
@@ -411,8 +375,9 @@ restart:
return NDBT_FAILED;
}
- if( pOp->readTuplesExclusive(parallelism) ) {
- ERR(pTrans->getNdbError());
+ if( pOp->readTuples(NdbOperation::LM_Exclusive, flags,
+ parallelism))
+ {
closeTransaction(pNdb);
return NDBT_FAILED;
}
@@ -429,15 +394,18 @@ restart:
check = pTrans->execute(NoCommit, AbortOnError);
if( check == -1 ) {
const NdbError err = pTrans->getNdbError();
- ERR(err);
- closeTransaction(pNdb);
if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
NdbSleep_MilliSleep(50);
+ retryAttempt++;
continue;
}
+ ERR(err);
+ closeTransaction(pNdb);
return NDBT_FAILED;
}
-
+
// Abort after 1-100 or 1-records rows
int ranVal = rand();
int abortCount = ranVal % (records == 0 ? 100 : records);
@@ -448,75 +416,114 @@ restart:
abortTrans = true;
}
+ int eof;
int rows = 0;
- while((check = pOp->nextResult(true)) == 0){
- do {
- rows++;
- NdbOperation* pUp = pOp->updateCurrentTuple();
- if(pUp == 0){
+ while((eof = pOp->nextResult(true)) == 0){
+ rows++;
+ if (calc.verifyRowValues(&row) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if (abortCount == rows && abortTrans == true){
+ ndbout << "Scan is aborted" << endl;
+ g_info << "Scan is aborted" << endl;
+ pOp->close();
+ if( check == -1 ) {
ERR(pTrans->getNdbError());
closeTransaction(pNdb);
return NDBT_FAILED;
}
- const int updates = calc.getUpdatesValue(&row) + 1;
- const int r = calc.getIdValue(&row);
- for(a = 0; a<tab.getNoOfColumns(); a++){
- if (tab.getColumn(a)->getPrimaryKey() == false){
- if(setValueForAttr(pUp, a, r, updates ) != 0){
- ERR(pTrans->getNdbError());
- closeTransaction(pNdb);
- return NDBT_FAILED;
- }
- }
- }
-
- if (rows == abortCount && abortTrans == true){
- g_info << "Scan is aborted" << endl;
- // This scan should be aborted
- closeTransaction(pNdb);
- return NDBT_OK;
- }
- } while((check = pOp->nextResult(false)) == 0);
-
- if(check != -1){
- check = pTrans->execute(Commit, AbortOnError);
- if(check != -1)
- m_latest_gci = pTrans->getGCI();
- pTrans->restart();
- }
-
- const NdbError err = pTrans->getNdbError();
- if( check == -1 ) {
+
closeTransaction(pNdb);
- ERR(err);
- if (err.status == NdbError::TemporaryError){
- NdbSleep_MilliSleep(50);
- goto restart;
- }
- return NDBT_FAILED;
+ return NDBT_OK;
}
}
-
- const NdbError err = pTrans->getNdbError();
- if( check == -1 ) {
- closeTransaction(pNdb);
- ERR(err);
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
if (err.status == NdbError::TemporaryError){
+ ERR_INFO(err);
+ closeTransaction(pNdb);
NdbSleep_MilliSleep(50);
- goto restart;
+ switch (err.code){
+ case 488:
+ case 245:
+ case 490:
+ // Too many active scans, no limit on number of retry attempts
+ break;
+ default:
+ retryAttempt++;
+ }
+ continue;
}
+ ERR(err);
+ closeTransaction(pNdb);
return NDBT_FAILED;
}
-
+
closeTransaction(pNdb);
+
+ g_info << rows << " rows have been read" << endl;
+ if (records != 0 && rows != records){
+ g_err << "Check expected number of records failed" << endl
+ << " expected=" << records <<", " << endl
+ << " read=" << rows << endl;
+ return NDBT_FAILED;
+ }
- g_info << rows << " rows have been updated" << endl;
return NDBT_OK;
}
return NDBT_FAILED;
}
int
+HugoTransactions::scanUpdateRecords(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism){
+
+ return scanUpdateRecords(pNdb,
+ (NdbScanOperation::ScanFlag)0,
+ records, abortPercent, parallelism);
+}
+
+// Scan all records exclusive and update
+// them one by one
+int
+HugoTransactions::scanUpdateRecords1(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism){
+ return scanUpdateRecords(pNdb,
+ (NdbScanOperation::ScanFlag)0,
+ records, abortPercent, 1);
+}
+
+// Scan all records exclusive and update
+// them batched by asking nextScanResult to
+// give us all cached records before fetching new
+// records from db
+int
+HugoTransactions::scanUpdateRecords2(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism){
+ return scanUpdateRecords(pNdb, (NdbScanOperation::ScanFlag)0,
+ records, abortPercent, parallelism);
+}
+
+int
+HugoTransactions::scanUpdateRecords3(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism)
+{
+ return scanUpdateRecords(pNdb, (NdbScanOperation::ScanFlag)0,
+ records, abortPercent, parallelism);
+}
+
+int
HugoTransactions::loadTable(Ndb* pNdb,
int records,
int batch,
@@ -524,7 +531,22 @@ HugoTransactions::loadTable(Ndb* pNdb,
int doSleep,
bool oneTrans,
int value,
- bool abort){
+ bool abort)
+{
+ return loadTableStartFrom(pNdb, 0, records, batch, allowConstraintViolation,
+ doSleep, oneTrans, value, abort);
+}
+
+int
+HugoTransactions::loadTableStartFrom(Ndb* pNdb,
+ int startFrom,
+ int records,
+ int batch,
+ bool allowConstraintViolation,
+ int doSleep,
+ bool oneTrans,
+ int value,
+ bool abort){
int check;
int retryAttempt = 0;
int retryMax = 5;
@@ -543,8 +565,9 @@ HugoTransactions::loadTable(Ndb* pNdb,
<< " -> rows/commit = " << batch << endl;
}
+ Uint32 orgbatch = batch;
g_info << "|- Inserting records..." << endl;
- for (int c=0 ; c<records ; ){
+ for (int c=0 ; c<records; ){
bool closeTrans = true;
if(c + batch > records)
@@ -578,7 +601,7 @@ HugoTransactions::loadTable(Ndb* pNdb,
}
}
- if(pkInsertRecord(pNdb, c, batch, value) != NDBT_OK)
+ if(pkInsertRecord(pNdb, c + startFrom, batch, value) != NDBT_OK)
{
ERR(pTrans->getNdbError());
closeTransaction(pNdb);
@@ -625,6 +648,7 @@ HugoTransactions::loadTable(Ndb* pNdb,
ERR(err);
NdbSleep_MilliSleep(50);
retryAttempt++;
+ batch = 1;
continue;
break;
@@ -670,7 +694,14 @@ HugoTransactions::loadTable(Ndb* pNdb,
int
HugoTransactions::fillTable(Ndb* pNdb,
- int batch){
+ int batch){
+ return fillTableStartFrom(pNdb, 0, batch);
+}
+
+int
+HugoTransactions::fillTableStartFrom(Ndb* pNdb,
+ int startFrom,
+ int batch){
int check;
int retryAttempt = 0;
int retryMax = 5;
@@ -688,7 +719,7 @@ HugoTransactions::fillTable(Ndb* pNdb,
<< " -> rows/commit = " << batch << endl;
}
- for (int c=0 ; ; ){
+ for (int c=startFrom ; ; ){
if (retryAttempt >= retryMax){
g_info << "Record " << c << " could not be inserted, has retried "
diff --git a/storage/ndb/test/src/Makefile.am b/storage/ndb/test/src/Makefile.am
index a025579cb72..aa486108235 100644
--- a/storage/ndb/test/src/Makefile.am
+++ b/storage/ndb/test/src/Makefile.am
@@ -24,7 +24,7 @@ libNDBT_a_SOURCES = \
NdbRestarter.cpp NdbRestarts.cpp NDBT_Output.cpp \
NdbBackup.cpp NdbConfig.cpp NdbGrep.cpp NDBT_Table.cpp \
NdbSchemaCon.cpp NdbSchemaOp.cpp getarg.c \
- CpcClient.cpp NdbMixRestarter.cpp NDBT_Thread.cpp
+ CpcClient.cpp NdbMixRestarter.cpp NDBT_Thread.cpp dbutil.cpp
INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/common/mgmcommon -I$(top_srcdir)/storage/ndb/include/mgmcommon -I$(top_srcdir)/storage/ndb/include/kernel -I$(top_srcdir)/storage/ndb/src/mgmapi
diff --git a/storage/ndb/test/src/NDBT_Thread.cpp b/storage/ndb/test/src/NDBT_Thread.cpp
index 56cf2f6815b..ff6785724ba 100644
--- a/storage/ndb/test/src/NDBT_Thread.cpp
+++ b/storage/ndb/test/src/NDBT_Thread.cpp
@@ -131,7 +131,7 @@ NDBT_Thread::exit()
m_state = Exit;
signal();
unlock();
-};
+}
void
NDBT_Thread::join()
diff --git a/storage/ndb/test/src/NdbRestarts.cpp b/storage/ndb/test/src/NdbRestarts.cpp
index 6ec520887b5..86e71f4b3fc 100644
--- a/storage/ndb/test/src/NdbRestarts.cpp
+++ b/storage/ndb/test/src/NdbRestarts.cpp
@@ -607,6 +607,7 @@ NFDuringNR_codes[] = {
5026,
7139,
7132,
+ 5045,
//LCP
8000,
diff --git a/storage/ndb/test/src/UtilTransactions.cpp b/storage/ndb/test/src/UtilTransactions.cpp
index 5a408140c8e..776ffd176b3 100644
--- a/storage/ndb/test/src/UtilTransactions.cpp
+++ b/storage/ndb/test/src/UtilTransactions.cpp
@@ -42,38 +42,9 @@ UtilTransactions::UtilTransactions(Ndb* ndb,
int
UtilTransactions::clearTable(Ndb* pNdb,
- int records,
- int parallelism){
- if(m_defaultClearMethod == 1){
- return clearTable1(pNdb, records, parallelism);
- } else if(m_defaultClearMethod == 2){
- return clearTable2(pNdb, records, parallelism);
- } else {
- return clearTable3(pNdb, records, parallelism);
- }
-}
-
-
-int
-UtilTransactions::clearTable1(Ndb* pNdb,
- int records,
- int parallelism)
-{
- return clearTable3(pNdb, records, 1);
-}
-
-int
-UtilTransactions::clearTable2(Ndb* pNdb,
- int records,
- int parallelism)
-{
- return clearTable3(pNdb, records, parallelism);
-}
-
-int
-UtilTransactions::clearTable3(Ndb* pNdb,
- int records,
- int parallelism){
+ NdbScanOperation::ScanFlag flags,
+ int records,
+ int parallelism){
// Scan all records exclusive and delete
// them one by one
int retryAttempt = 0;
@@ -116,7 +87,7 @@ UtilTransactions::clearTable3(Ndb* pNdb,
goto failed;
}
- if( pOp->readTuplesExclusive(par) ) {
+ if( pOp->readTuples(NdbOperation::LM_Exclusive, flags, par) ) {
err = pTrans->getNdbError();
goto failed;
}
@@ -180,6 +151,43 @@ UtilTransactions::clearTable3(Ndb* pNdb,
}
int
+UtilTransactions::clearTable(Ndb* pNdb,
+ int records,
+ int parallelism){
+
+ return clearTable(pNdb, (NdbScanOperation::ScanFlag)0,
+ records, parallelism);
+}
+
+
+int
+UtilTransactions::clearTable1(Ndb* pNdb,
+ int records,
+ int parallelism)
+{
+ return clearTable(pNdb, (NdbScanOperation::ScanFlag)0,
+ records, 1);
+}
+
+int
+UtilTransactions::clearTable2(Ndb* pNdb,
+ int records,
+ int parallelism)
+{
+ return clearTable(pNdb, (NdbScanOperation::ScanFlag)0,
+ records, parallelism);
+}
+
+int
+UtilTransactions::clearTable3(Ndb* pNdb,
+ int records,
+ int parallelism)
+{
+ return clearTable(pNdb, (NdbScanOperation::ScanFlag)0,
+ records, parallelism);
+}
+
+int
UtilTransactions::copyTableData(Ndb* pNdb,
const char* destName){
// Scan all records and copy
diff --git a/storage/ndb/test/tools/Makefile.am b/storage/ndb/test/tools/Makefile.am
index 1683d4d84ae..da715caa1cb 100644
--- a/storage/ndb/test/tools/Makefile.am
+++ b/storage/ndb/test/tools/Makefile.am
@@ -13,7 +13,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index ndb_cpcc listen_event eventlog rep_latency
+ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index ndb_cpcc listen_event eventlog rep_latency ndb_connect
# transproxy
@@ -35,6 +35,7 @@ ndb_cpcc_SOURCES = cpcc.cpp
listen_event_SOURCES = listen.cpp
eventlog_SOURCES = log_listner.cpp
rep_latency_SOURCES = rep_latency.cpp
+ndb_connect_SOURCES = connect.cpp
include $(top_srcdir)/storage/ndb/config/common.mk.am
include $(top_srcdir)/storage/ndb/config/type_ndbapitest.mk.am
diff --git a/storage/ndb/test/tools/connect.cpp b/storage/ndb/test/tools/connect.cpp
new file mode 100644
index 00000000000..2d3ac34d3e8
--- /dev/null
+++ b/storage/ndb/test/tools/connect.cpp
@@ -0,0 +1,152 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+#include <NDBT.hpp>
+#include <NdbApi.hpp>
+#include <NdbSleep.h>
+
+NDB_STD_OPTS_VARS;
+
+static int _loop = 25;
+static int _sleep = 25;
+static int _drop = 1;
+
+typedef uchar* gptr;
+
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "loop", 'l', "loops",
+ (gptr*) &_loop, (gptr*) &_loop, 0,
+ GET_INT, REQUIRED_ARG, _loop, 0, 0, 0, 0, 0 },
+ { "sleep", 's', "Sleep (ms) between connection attempt",
+ (gptr*) &_sleep, (gptr*) &_sleep, 0,
+ GET_INT, REQUIRED_ARG, _sleep, 0, 0, 0, 0, 0 },
+ { "drop", 'd',
+ "Drop event operations before disconnect (0 = no, 1 = yes, else rand",
+ (gptr*) &_drop, (gptr*) &_drop, 0,
+ GET_INT, REQUIRED_ARG, _drop, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static void usage()
+{
+ char desc[] = "This program connects to ndbd, and then disconnects\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_desc.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+
+ for (int i = 0; i<_loop; i++)
+ {
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ if (con.wait_until_ready(30,30) != 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, "TEST_DB");
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Vector<NdbEventOperation*> ops;
+ const NdbDictionary::Dictionary * dict= MyNdb.getDictionary();
+ for (int j = 0; j < argc; j++)
+ {
+ const NdbDictionary::Table * pTab = dict->getTable(argv[j]);
+ if (pTab == 0)
+ {
+ ndbout_c("Failed to retreive table: \"%s\"", argv[j]);
+ }
+
+ BaseString tmp;
+ tmp.appfmt("EV-%s", argv[j]);
+ NdbEventOperation* pOp = MyNdb.createEventOperation(tmp.c_str());
+ if ( pOp == NULL )
+ {
+ ndbout << "Event operation creation failed: " <<
+ MyNdb.getNdbError() << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ for (int a = 0; a < pTab->getNoOfColumns(); a++)
+ {
+ pOp->getValue(pTab->getColumn(a)->getName());
+ pOp->getPreValue(pTab->getColumn(a)->getName());
+ }
+
+ if (pOp->execute())
+ {
+ ndbout << "operation execution failed: " << pOp->getNdbError()
+ << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ ops.push_back(pOp);
+ }
+
+ if (_sleep)
+ {
+ NdbSleep_MilliSleep(10 + rand() % _sleep);
+ }
+
+ for (Uint32 i = 0; i<ops.size(); i++)
+ {
+ switch(_drop){
+ case 0:
+ break;
+ do_drop:
+ case 1:
+ if (MyNdb.dropEventOperation(ops[i]))
+ {
+ ndbout << "drop event operation failed "
+ << MyNdb.getNdbError() << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ break;
+ default:
+ if ((rand() % 100) > 50)
+ goto do_drop;
+ }
+ }
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+template class Vector<NdbEventOperation*>;
diff --git a/storage/ndb/tools/restore/Restore.cpp b/storage/ndb/tools/restore/Restore.cpp
index a7d8a9d10d9..f599bb21978 100644
--- a/storage/ndb/tools/restore/Restore.cpp
+++ b/storage/ndb/tools/restore/Restore.cpp
@@ -534,6 +534,88 @@ TupleS::prepareRecord(TableS & tab){
return true;
}
+int
+RestoreDataIterator::readTupleData(Uint32 *buf_ptr, Uint32 *ptr,
+ Uint32 dataLength)
+{
+ while (ptr + 2 < buf_ptr + dataLength)
+ {
+ typedef BackupFormat::DataFile::VariableData VarData;
+ VarData * data = (VarData *)ptr;
+ Uint32 sz = ntohl(data->Sz);
+ Uint32 attrId = ntohl(data->Id); // column_no
+
+ AttributeData * attr_data = m_tuple.getData(attrId);
+ const AttributeDesc * attr_desc = m_tuple.getDesc(attrId);
+
+ // just a reminder - remove when backwards compat implemented
+ if (m_currentTable->backupVersion < MAKE_VERSION(5,1,3) &&
+ attr_desc->m_column->getNullable())
+ {
+ const Uint32 ind = attr_desc->m_nullBitIndex;
+ if(BitmaskImpl::get(m_currentTable->m_nullBitmaskSize,
+ buf_ptr,ind))
+ {
+ attr_data->null = true;
+ attr_data->void_value = NULL;
+ continue;
+ }
+ }
+
+ if (m_currentTable->backupVersion < MAKE_VERSION(5,1,3))
+ {
+ sz *= 4;
+ }
+
+ attr_data->null = false;
+ attr_data->void_value = &data->Data[0];
+ attr_data->size = sz;
+
+ //if (m_currentTable->getTableId() >= 2) { ndbout << "var off=" << ptr-buf_ptr << " attrId=" << attrId << endl; }
+
+ /**
+ * Compute array size
+ */
+ const Uint32 arraySize = sz / (attr_desc->size / 8);
+ assert(arraySize <= attr_desc->arraySize);
+
+ //convert the length of blob(v1) and text(v1)
+ if(!m_hostByteOrder
+ && (attr_desc->m_column->getType() == NdbDictionary::Column::Blob
+ || attr_desc->m_column->getType() == NdbDictionary::Column::Text)
+ && attr_desc->m_column->getArrayType() == NdbDictionary::Column::ArrayTypeFixed)
+ {
+ char* p = (char*)&attr_data->u_int64_value[0];
+ Uint64 x;
+ memcpy(&x, p, sizeof(Uint64));
+ x = Twiddle64(x);
+ memcpy(p, &x, sizeof(Uint64));
+ }
+
+ //convert datetime type
+ if(!m_hostByteOrder
+ && attr_desc->m_column->getType() == NdbDictionary::Column::Datetime)
+ {
+ char* p = (char*)&attr_data->u_int64_value[0];
+ Uint64 x;
+ memcpy(&x, p, sizeof(Uint64));
+ x = Twiddle64(x);
+ memcpy(p, &x, sizeof(Uint64));
+ }
+
+ if(!Twiddle(attr_desc, attr_data, attr_desc->arraySize))
+ {
+ return -1;
+ }
+
+ ptr += ((sz + 3) >> 2) + 2;
+ }
+
+ assert(ptr == buf_ptr + dataLength);
+
+ return 0;
+}
+
const TupleS *
RestoreDataIterator::getNextTuple(int & res)
{
@@ -630,78 +712,8 @@ RestoreDataIterator::getNextTuple(int & res)
attr_data->void_value = NULL;
}
- while (ptr + 2 < buf_ptr + dataLength) {
- typedef BackupFormat::DataFile::VariableData VarData;
- VarData * data = (VarData *)ptr;
- Uint32 sz = ntohl(data->Sz);
- Uint32 attrId = ntohl(data->Id); // column_no
-
- AttributeData * attr_data = m_tuple.getData(attrId);
- const AttributeDesc * attr_desc = m_tuple.getDesc(attrId);
-
- // just a reminder - remove when backwards compat implemented
- if(m_currentTable->backupVersion < MAKE_VERSION(5,1,3) &&
- attr_desc->m_column->getNullable()){
- const Uint32 ind = attr_desc->m_nullBitIndex;
- if(BitmaskImpl::get(m_currentTable->m_nullBitmaskSize,
- buf_ptr,ind)){
- attr_data->null = true;
- attr_data->void_value = NULL;
- continue;
- }
- }
-
- if (m_currentTable->backupVersion < MAKE_VERSION(5,1,3))
- {
- sz *= 4;
- }
-
- attr_data->null = false;
- attr_data->void_value = &data->Data[0];
- attr_data->size = sz;
-
- //if (m_currentTable->getTableId() >= 2) { ndbout << "var off=" << ptr-buf_ptr << " attrId=" << attrId << endl; }
-
- /**
- * Compute array size
- */
- const Uint32 arraySize = sz / (attr_desc->size / 8);
- assert(arraySize <= attr_desc->arraySize);
-
- //convert the length of blob(v1) and text(v1)
- if(!m_hostByteOrder
- && (attr_desc->m_column->getType() == NdbDictionary::Column::Blob
- || attr_desc->m_column->getType() == NdbDictionary::Column::Text)
- && attr_desc->m_column->getArrayType() == NdbDictionary::Column::ArrayTypeFixed)
- {
- char* p = (char*)&attr_data->u_int64_value[0];
- Uint64 x;
- memcpy(&x, p, sizeof(Uint64));
- x = Twiddle64(x);
- memcpy(p, &x, sizeof(Uint64));
- }
-
- //convert datetime type
- if(!m_hostByteOrder
- && attr_desc->m_column->getType() == NdbDictionary::Column::Datetime)
- {
- char* p = (char*)&attr_data->u_int64_value[0];
- Uint64 x;
- memcpy(&x, p, sizeof(Uint64));
- x = Twiddle64(x);
- memcpy(p, &x, sizeof(Uint64));
- }
-
- if(!Twiddle(attr_desc, attr_data, attr_desc->arraySize))
- {
- res = -1;
- return NULL;
- }
-
- ptr += ((sz + 3) >> 2) + 2;
- }
-
- assert(ptr == buf_ptr + dataLength);
+ if ((res = readTupleData(buf_ptr, ptr, dataLength)))
+ return NULL;
m_count ++;
res = 0;
diff --git a/storage/ndb/tools/restore/Restore.hpp b/storage/ndb/tools/restore/Restore.hpp
index 5455fa17aa0..f6de9245509 100644
--- a/storage/ndb/tools/restore/Restore.hpp
+++ b/storage/ndb/tools/restore/Restore.hpp
@@ -355,6 +355,10 @@ public:
bool validateFragmentFooter();
const TupleS *getNextTuple(int & res);
+
+private:
+
+ int readTupleData(Uint32 *buf_ptr, Uint32 *ptr, Uint32 dataLength);
};
class LogEntry {