mirror of
https://github.com/MariaDB/server.git
synced 2025-01-24 15:54:37 +01:00
92e68d49d7
Changed header to GPL version 2 only client/mysqlslap.c: Changed header to GPL version 2 only include/atomic/nolock.h: Changed header to GPL version 2 only include/atomic/rwlock.h: Changed header to GPL version 2 only include/atomic/x86-gcc.h: Changed header to GPL version 2 only include/atomic/x86-msvc.h: Changed header to GPL version 2 only include/my_atomic.h: Changed header to GPL version 2 only include/my_trie.h: Changed header to GPL version 2 only include/my_vle.h: Changed header to GPL version 2 only include/mysql/plugin.h: Changed header to GPL version 2 only mysys/my_atomic.c: Changed header to GPL version 2 only mysys/my_getncpus.c: Changed header to GPL version 2 only mysys/my_memmem.c: Changed header to GPL version 2 only mysys/my_vle.c: Changed header to GPL version 2 only mysys/trie.c: Changed header to GPL version 2 only plugin/Makefile.am: Changed header to GPL version 2 only server-tools/instance-manager/IMService.h: Changed header to GPL version 2 only server-tools/instance-manager/WindowsService.h: Changed header to GPL version 2 only server-tools/instance-manager/exit_codes.h: Changed header to GPL version 2 only server-tools/instance-manager/user_management_commands.h: Changed header to GPL version 2 only sql/authors.h: Changed header to GPL version 2 only sql/contributors.h: Changed header to GPL version 2 only sql/event_data_objects.cc: Changed header to GPL version 2 only sql/event_data_objects.h: Changed header to GPL version 2 only sql/event_db_repository.cc: Changed header to GPL version 2 only sql/event_db_repository.h: Changed header to GPL version 2 only sql/event_queue.cc: Changed header to GPL version 2 only sql/event_queue.h: Changed header to GPL version 2 only sql/event_scheduler.cc: Changed header to GPL version 2 only sql/event_scheduler.h: Changed header to GPL version 2 only sql/events.cc: Changed header to GPL version 2 only sql/events.h: Changed header to GPL version 2 only sql/ha_ndbcluster_binlog.cc: Changed header to GPL version 2 only sql/ha_ndbcluster_binlog.h: Changed header to GPL version 2 only sql/ha_ndbcluster_tables.h: Changed header to GPL version 2 only sql/ha_partition.cc: Changed header to GPL version 2 only sql/ha_partition.h: Changed header to GPL version 2 only sql/item_xmlfunc.cc: Changed header to GPL version 2 only sql/item_xmlfunc.h: Changed header to GPL version 2 only sql/log.h: Changed header to GPL version 2 only sql/partition_element.h: Changed header to GPL version 2 only sql/partition_info.cc: Changed header to GPL version 2 only sql/partition_info.h: Changed header to GPL version 2 only sql/rpl_filter.cc: Changed header to GPL version 2 only sql/rpl_filter.h: Changed header to GPL version 2 only sql/rpl_injector.cc: Changed header to GPL version 2 only sql/rpl_injector.h: Changed header to GPL version 2 only sql/rpl_mi.cc: Changed header to GPL version 2 only sql/rpl_mi.h: Changed header to GPL version 2 only sql/rpl_rli.cc: Changed header to GPL version 2 only sql/rpl_rli.h: Changed header to GPL version 2 only sql/rpl_tblmap.cc: Changed header to GPL version 2 only sql/rpl_tblmap.h: Changed header to GPL version 2 only sql/rpl_utility.cc: Changed header to GPL version 2 only sql/rpl_utility.h: Changed header to GPL version 2 only sql/sql_binlog.cc: Changed header to GPL version 2 only sql/sql_partition.cc: Changed header to GPL version 2 only sql/sql_partition.h: Changed header to GPL version 2 only sql/sql_plugin.cc: Changed header to GPL version 2 only sql/sql_plugin.h: Changed header to GPL version 2 only sql/sql_servers.cc: Changed header to GPL version 2 only sql/sql_servers.h: Changed header to GPL version 2 only sql/sql_tablespace.cc: Changed header to GPL version 2 only sql/sql_yacc.yy.bak: Changed header to GPL version 2 only storage/Makefile.am: Changed header to GPL version 2 only storage/archive/Makefile.am: Changed header to GPL version 2 only storage/blackhole/Makefile.am: Changed header to GPL version 2 only storage/csv/Makefile.am: Changed header to GPL version 2 only storage/example/Makefile.am: Changed header to GPL version 2 only storage/federated/Makefile.am: Changed header to GPL version 2 only storage/innobase/handler/Makefile.am: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/AllocNodeId.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/CreateFilegroup.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/CreateFilegroupImpl.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/CreateObj.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/DictObjOp.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/DihFragCount.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/DropFilegroup.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/DropFilegroupImpl.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/DropObj.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/Extent.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/LgmanContinueB.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/PgmanContinueB.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/RestoreContinueB.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/RestoreImpl.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/RouteOrd.hpp: Changed header to GPL version 2 only storage/ndb/include/kernel/signaldata/TsmanContinueB.hpp: Changed header to GPL version 2 only storage/ndb/include/ndbapi/NdbIndexStat.hpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/mgmapi_logevent/mgmapi_logevent.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/mgmapi_logevent_dual/mgmapi_logevent_dual.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_async/ndbapi_async.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_async1/ndbapi_async1.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_retries/ndbapi_retries.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_scan/ndbapi_scan.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_simple/ndbapi_simple.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_simple_dual/ndbapi_simple_dual.cpp: Changed header to GPL version 2 only storage/ndb/ndbapi-examples/ndbapi_simple_index/ndbapi_simple_index.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbdih/printSysfile.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbtup/DbtupVarAlloc.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbtup/Undo_buffer.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbtup/Undo_buffer.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbtup/tuppage.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbtup/tuppage.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/dbtux/DbtuxStat.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/diskpage.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/lgman.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/lgman.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/pgman.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/pgman.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/print_file.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/record_types.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/restore.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/restore.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/tsman.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/blocks/tsman.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/DLCFifoList.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/DLCHashTable.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/DynArr256.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/DynArr256.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/KeyTable2Ref.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/LinearPool.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/NdbdSuperPool.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/NdbdSuperPool.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/Pool.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/Pool.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/RWPool.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/RWPool.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/Rope.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/SLFifoList.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/WOPool.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/WOPool.hpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/bench_pool.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/ndbd_malloc_impl.cpp: Changed header to GPL version 2 only storage/ndb/src/kernel/vm/ndbd_malloc_impl.hpp: Changed header to GPL version 2 only storage/ndb/src/mgmsrv/ParamInfo.cpp: Changed header to GPL version 2 only storage/ndb/src/ndbapi/NdbIndexStat.cpp: Changed header to GPL version 2 only storage/ndb/test/ndbapi/testIndexStat.cpp: Changed header to GPL version 2 only storage/ndb/test/tools/listen.cpp: Changed header to GPL version 2 only storage/ndb/tools/restore/ndb_nodegroup_map.h: Changed header to GPL version 2 only strings/my_strchr.c: Changed header to GPL version 2 only unittest/mysys/base64-t.c: Changed header to GPL version 2 only unittest/mysys/bitmap-t.c: Changed header to GPL version 2 only unittest/mysys/my_atomic-t.c: Changed header to GPL version 2 only unittest/mytap/tap.c: Changed header to GPL version 2 only unittest/mytap/tap.h: Changed header to GPL version 2 only win/Makefile.am: Changed header to GPL version 2 only
1405 lines
33 KiB
C++
1405 lines
33 KiB
C++
/* Copyright (C) 2005 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_global.h>
|
|
#include <ndb_opts.h>
|
|
#include <NdbApi.hpp>
|
|
#include <NdbIndexStat.hpp>
|
|
#include <NdbTest.hpp>
|
|
#include <my_sys.h>
|
|
#include <ndb_version.h>
|
|
#include <math.h>
|
|
|
|
/*
|
|
* Sample results:
|
|
*
|
|
* 0. err pct: count: 1000 min: -99.99 max: 99.92 avg: 6.88 stddev: 27.61
|
|
*
|
|
* 0. baseline with same options as handler
|
|
*/
|
|
|
|
#undef min
|
|
#undef max
|
|
#define min(a, b) ((a) <= (b) ? (a) : (b))
|
|
#define max(a, b) ((a) >= (b) ? (a) : (b))
|
|
|
|
inline NdbOut&
|
|
NdbOut::operator<<(double x)
|
|
{
|
|
char buf[100];
|
|
sprintf(buf, "%.2f", x);
|
|
*this << buf;
|
|
return *this;
|
|
}
|
|
|
|
struct Opts {
|
|
int loglevel;
|
|
uint seed;
|
|
uint loop;
|
|
uint rows;
|
|
uint ops;
|
|
uint nullkeys;
|
|
uint dupkeys;
|
|
uint scanpct;
|
|
uint eqscans;
|
|
uint dupscans;
|
|
my_bool keeptable;
|
|
my_bool loaddata;
|
|
my_bool nochecks;
|
|
my_bool abort;
|
|
// internal
|
|
uint tryhard;
|
|
Opts() :
|
|
loglevel(0),
|
|
seed(-1),
|
|
loop(1),
|
|
rows(100000),
|
|
ops(1000),
|
|
nullkeys(10),
|
|
dupkeys(1000),
|
|
scanpct(5),
|
|
eqscans(50),
|
|
dupscans(10),
|
|
keeptable(false),
|
|
loaddata(true),
|
|
nochecks(false),
|
|
abort(false),
|
|
// internal
|
|
tryhard(20)
|
|
{}
|
|
};
|
|
|
|
static Opts g_opts;
|
|
const char* g_progname = "testIndexStat";
|
|
static uint g_loop = 0;
|
|
|
|
static const char* g_tabname = "ts0";
|
|
static const char* g_indname = "ts0x1";
|
|
static const char g_numattrs = 3;
|
|
static const uint g_charlen = 10;
|
|
static const char* g_csname = "latin1_swedish_ci";
|
|
static CHARSET_INFO* g_cs;
|
|
|
|
// value and bound ranges
|
|
static uint g_val_b_max = 10;
|
|
static uint g_bnd_b_max = 20;
|
|
static const char* g_val_c_char = "bcd";
|
|
static const char* g_bnd_c_char = "abcde";
|
|
static uint g_val_d_max = 100;
|
|
static uint g_bnd_d_max = 200;
|
|
|
|
static Ndb_cluster_connection* g_ncc = 0;
|
|
static Ndb* g_ndb = 0;
|
|
static NdbDictionary::Dictionary* g_dic = 0;
|
|
static const NdbDictionary::Table* g_tab = 0;
|
|
static const NdbDictionary::Index* g_ind = 0;
|
|
|
|
static NdbIndexStat* g_stat = 0;
|
|
|
|
static NdbTransaction* g_con = 0;
|
|
static NdbOperation* g_op = 0;
|
|
static NdbScanOperation* g_scan_op = 0;
|
|
static NdbIndexScanOperation* g_rangescan_op = 0;
|
|
|
|
static uint
|
|
urandom()
|
|
{
|
|
uint r = (uint)random();
|
|
return r;
|
|
}
|
|
|
|
static uint
|
|
urandom(uint m)
|
|
{
|
|
if (m == 0)
|
|
return 0;
|
|
uint r = urandom();
|
|
r = r % m;
|
|
return r;
|
|
}
|
|
|
|
static int& g_loglevel = g_opts.loglevel; // default log level
|
|
|
|
#define chkdb(x) \
|
|
do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; errdb(); if (g_opts.abort) abort(); return -1; } while (0)
|
|
|
|
#define chkrc(x) \
|
|
do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; if (g_opts.abort) abort(); return -1; } while (0)
|
|
|
|
#define reqrc(x) \
|
|
do { if (likely(x)) break; ndbout << "line " << __LINE__ << " ASSERT " << #x << endl; abort(); } while (0)
|
|
|
|
#define llx(n, x) \
|
|
do { if (likely(g_loglevel < n)) break; ndbout << x << endl; } while (0)
|
|
|
|
#define ll0(x) llx(0, x)
|
|
#define ll1(x) llx(1, x)
|
|
#define ll2(x) llx(2, x)
|
|
#define ll3(x) llx(3, x)
|
|
|
|
static void
|
|
errdb()
|
|
{
|
|
uint any = 0;
|
|
// g_ncc return no error...
|
|
if (g_ndb != 0) {
|
|
const NdbError& e = g_ndb->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " ndb: error " << e);
|
|
}
|
|
if (g_dic != 0) {
|
|
const NdbError& e = g_dic->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " dic: error " << e);
|
|
}
|
|
if (g_con != 0) {
|
|
const NdbError& e = g_con->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " con: error " << e);
|
|
}
|
|
if (g_op != 0) {
|
|
const NdbError& e = g_op->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " op: error " << e);
|
|
}
|
|
if (g_scan_op != 0) {
|
|
const NdbError& e = g_scan_op->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " scan_op: error " << e);
|
|
}
|
|
if (g_rangescan_op != 0) {
|
|
const NdbError& e = g_rangescan_op->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " rangescan_op: error " << e);
|
|
}
|
|
if (g_stat != 0) {
|
|
const NdbError& e = g_stat->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " stat: error " << e);
|
|
}
|
|
if (! any)
|
|
ll0("unknown db error");
|
|
}
|
|
|
|
// create table ts0 (
|
|
// a int unsigned, b smallint not null, c varchar(10), d int unsigned,
|
|
// primary key using hash (a), index (b, c, d) )
|
|
|
|
static int
|
|
createtable()
|
|
{
|
|
NdbDictionary::Table tab(g_tabname);
|
|
tab.setLogging(false);
|
|
{
|
|
NdbDictionary::Column col("a");
|
|
col.setType(NdbDictionary::Column::Unsigned);
|
|
col.setPrimaryKey(true);
|
|
tab.addColumn(col);
|
|
}
|
|
{
|
|
NdbDictionary::Column col("b");
|
|
col.setType(NdbDictionary::Column::Smallint);
|
|
col.setNullable(false);
|
|
tab.addColumn(col);
|
|
}
|
|
{
|
|
NdbDictionary::Column col("c");
|
|
col.setType(NdbDictionary::Column::Varchar);
|
|
col.setLength(g_charlen);
|
|
col.setCharset(g_cs);
|
|
col.setNullable(true);
|
|
tab.addColumn(col);
|
|
}
|
|
{
|
|
NdbDictionary::Column col("d");
|
|
col.setType(NdbDictionary::Column::Unsigned);
|
|
col.setNullable(true);
|
|
tab.addColumn(col);
|
|
}
|
|
NdbDictionary::Index ind(g_indname);
|
|
ind.setTable(g_tabname);
|
|
ind.setType(NdbDictionary::Index::OrderedIndex);
|
|
ind.setLogging(false);
|
|
ind.addColumnName("b");
|
|
ind.addColumnName("c");
|
|
ind.addColumnName("d");
|
|
g_dic = g_ndb->getDictionary();
|
|
if (! g_opts.keeptable) {
|
|
if (g_dic->getTable(g_tabname) != 0)
|
|
chkdb(g_dic->dropTable(g_tabname) == 0);
|
|
chkdb(g_dic->createTable(tab) == 0);
|
|
chkdb(g_dic->createIndex(ind) == 0);
|
|
} else {
|
|
if (g_dic->getTable(g_tabname) == 0) {
|
|
chkdb(g_dic->createTable(tab) == 0);
|
|
chkdb(g_dic->createIndex(ind) == 0);
|
|
} else
|
|
g_opts.loaddata = false;
|
|
}
|
|
chkdb((g_tab = g_dic->getTable(g_tabname)) != 0);
|
|
chkdb((g_ind = g_dic->getIndex(g_indname, g_tabname)) != 0);
|
|
g_dic = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
droptable()
|
|
{
|
|
g_dic = g_ndb->getDictionary();
|
|
if (! g_opts.keeptable)
|
|
chkdb(g_dic->dropTable(g_tabname) == 0);
|
|
g_dic = 0;
|
|
return 0;
|
|
}
|
|
|
|
struct Val {
|
|
Int16 b;
|
|
bool c_null;
|
|
uchar c[1 + g_charlen];
|
|
bool d_null;
|
|
Uint32 d;
|
|
// partial values for use in Bnd
|
|
uint numattrs;
|
|
void make(uint n = g_numattrs, bool is_val = true);
|
|
int cmp(const Val& val, uint n = g_numattrs) const;
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Val& val)
|
|
{
|
|
out << "[";
|
|
if (val.numattrs >= 1) {
|
|
out << val.b;
|
|
}
|
|
if (val.numattrs >= 2) {
|
|
out << " ";
|
|
if (val.c_null)
|
|
out << "NULL";
|
|
else {
|
|
char buf[1 + g_charlen];
|
|
sprintf(buf, "%.*s", val.c[0], &val.c[1]);
|
|
out << "'" << buf << "'";
|
|
}
|
|
}
|
|
if (val.numattrs >= 3) {
|
|
out << " ";
|
|
if (val.d_null)
|
|
out <<" NULL";
|
|
else
|
|
out << val.d;
|
|
}
|
|
out << "]";
|
|
return out;
|
|
}
|
|
|
|
void
|
|
Val::make(uint n, bool is_val)
|
|
{
|
|
if (n >= 1) {
|
|
uint b_max = is_val ? g_val_b_max : g_bnd_b_max;
|
|
b = (int)urandom(2 * b_max) - (int)b_max;
|
|
}
|
|
if (n >= 2) {
|
|
if (urandom(100) < g_opts.nullkeys)
|
|
c_null = 1;
|
|
else {
|
|
const char* c_char = is_val ? g_val_c_char : g_bnd_c_char;
|
|
// prefer shorter
|
|
uint len = urandom(urandom(g_charlen + 2));
|
|
c[0] = len;
|
|
uint j;
|
|
for (j = 0; j < len; j++) {
|
|
uint k = urandom(strlen(c_char));
|
|
c[1 + j] = c_char[k];
|
|
}
|
|
c_null = 0;
|
|
}
|
|
}
|
|
if (n >= 3) {
|
|
if (urandom(100) < g_opts.nullkeys)
|
|
d_null = 1;
|
|
else {
|
|
uint d_max = is_val ? g_val_d_max : g_bnd_d_max;
|
|
d = urandom(d_max);
|
|
d_null = 0;
|
|
}
|
|
}
|
|
numattrs = n;
|
|
}
|
|
|
|
int
|
|
Val::cmp(const Val& val, uint n) const
|
|
{
|
|
int k = 0;
|
|
if (k == 0 && n >= 1) {
|
|
if (b < val.b)
|
|
k = -1;
|
|
else if (b > val.b)
|
|
k = +1;
|
|
}
|
|
if (k == 0 && n >= 2) {
|
|
if (! c_null && ! val.c_null) {
|
|
const uchar* s1 = &c[1];
|
|
const uchar* s2 = &val.c[1];
|
|
const uint l1 = (uint)c[0];
|
|
const uint l2 = (uint)val.c[0];
|
|
assert(l1 <= g_charlen && l2 <= g_charlen);
|
|
k = g_cs->coll->strnncollsp(g_cs, s1, l1, s2, l2, 0);
|
|
} else if (! c_null) {
|
|
k = +1;
|
|
} else if (! val.c_null) {
|
|
k = -1;
|
|
}
|
|
}
|
|
if (k == 0 && n >= 3) {
|
|
if (! d_null && ! val.d_null) {
|
|
if (d < val.d)
|
|
k = -1;
|
|
else if (d > val.d)
|
|
k = +1;
|
|
} else if (! d_null) {
|
|
k = +1;
|
|
} else if (! val.d_null) {
|
|
k = -1;
|
|
}
|
|
}
|
|
return k;
|
|
}
|
|
|
|
struct Key {
|
|
Val val;
|
|
union {
|
|
bool flag;
|
|
uint count;
|
|
uint rpk;
|
|
};
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Key& key)
|
|
{
|
|
out << key.val << " info:" << key.count;
|
|
return out;
|
|
}
|
|
|
|
static Key* g_keys = 0;
|
|
static Key* g_sortkeys = 0;
|
|
static uint g_sortcount = 0;
|
|
static Key* g_minkey = 0;
|
|
static Key* g_maxkey = 0;
|
|
|
|
static void
|
|
freekeys()
|
|
{
|
|
if (g_keys != 0)
|
|
my_free((char*)g_keys, MYF(0));
|
|
if (g_sortkeys != 0)
|
|
my_free((char*)g_sortkeys, MYF(0));
|
|
g_keys = 0;
|
|
g_sortkeys = 0;
|
|
}
|
|
|
|
static int
|
|
allockeys()
|
|
{
|
|
freekeys();
|
|
size_t sz = sizeof(Key) * g_opts.rows;
|
|
g_keys = (Key*)my_malloc(sz, MYF(0));
|
|
g_sortkeys = (Key*)my_malloc(sz, MYF(0));
|
|
chkrc(g_keys != 0 && g_sortkeys != 0);
|
|
memset(g_keys, 0x1f, sz);
|
|
memset(g_sortkeys, 0x1f, sz);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
makekeys()
|
|
{
|
|
uint i;
|
|
for (i = 0; i < g_opts.rows; i++) {
|
|
Key& key = g_keys[i];
|
|
key.val.make();
|
|
key.flag = false; // mark for dup generation done
|
|
}
|
|
for (i = 0; i < g_opts.rows; i++) {
|
|
Key& key = g_keys[i];
|
|
if (key.flag)
|
|
continue;
|
|
key.flag = true;
|
|
uint fudge = 9;
|
|
uint n = (urandom(fudge * (g_opts.dupkeys - 100)) + 99) / 100;
|
|
uint k;
|
|
for (k = 1; k < n; k++) {
|
|
uint j = urandom(g_opts.rows);
|
|
do {
|
|
Key& dst = g_keys[j];
|
|
if (! dst.flag) {
|
|
dst.val = key.val;
|
|
dst.flag = true;
|
|
break;
|
|
}
|
|
} while (urandom(g_opts.tryhard) != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
insertdata()
|
|
{
|
|
const uint batch = 512;
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
uint i = 0;
|
|
while (i < g_opts.rows) {
|
|
chkdb((g_op = g_con->getNdbOperation(g_tab)) != 0);
|
|
chkdb(g_op->insertTuple() == 0);
|
|
Uint32 a = i;
|
|
const Val& val = g_keys[i].val;
|
|
const char* a_addr = (const char*)&a;
|
|
const char* b_addr = (const char*)&val.b;
|
|
const char* c_addr = ! val.c_null ? (const char*)val.c : 0;
|
|
const char* d_addr = ! val.d_null ? (const char*)&val.d : 0;
|
|
Uint32 no = 0;
|
|
chkdb(g_op->equal(no++, a_addr) == 0);
|
|
chkdb(g_op->setValue(no++, b_addr) == 0);
|
|
chkdb(g_op->setValue(no++, c_addr) == 0);
|
|
chkdb(g_op->setValue(no++, d_addr) == 0);
|
|
if (i++ % batch == 0) {
|
|
chkdb(g_con->execute(NdbTransaction::Commit) == 0);
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_op = 0;
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
}
|
|
}
|
|
chkdb(g_con->execute(NdbTransaction::Commit) == 0);
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_op = 0;
|
|
ll0(g_tabname << ": inserted " << g_opts.rows << " rows");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
countrows()
|
|
{
|
|
Uint64 rows = 0;
|
|
Uint64 r;
|
|
char* r_addr = (char*)&r;
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
chkdb((g_scan_op = g_con->getNdbScanOperation(g_tab)) != 0);
|
|
chkdb(g_scan_op->readTuples() == 0);
|
|
chkdb(g_scan_op->interpret_exit_last_row() == 0);
|
|
chkdb(g_scan_op->getValue(NdbDictionary::Column::ROW_COUNT, r_addr) != 0);
|
|
chkdb(g_con->execute(NdbTransaction::NoCommit) == 0);
|
|
while (1) {
|
|
int ret;
|
|
r = ~(Uint64)0;
|
|
chkdb((ret = g_scan_op->nextResult()) == 0 || ret == 1);
|
|
if (ret == 1)
|
|
break;
|
|
rows += r;
|
|
}
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_scan_op = 0;
|
|
g_opts.rows = rows;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
scandata()
|
|
{
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
chkdb((g_scan_op = g_con->getNdbScanOperation(g_tab)) != 0);
|
|
chkdb(g_scan_op->readTuples() == 0);
|
|
Uint32 a;
|
|
Val val;
|
|
char* a_addr = (char*)&a;
|
|
char* b_addr = (char*)&val.b;
|
|
char* c_addr = (char*)val.c;
|
|
char* d_addr = (char*)&val.d;
|
|
Uint32 no = 0;
|
|
NdbRecAttr* b_ra;
|
|
NdbRecAttr* c_ra;
|
|
NdbRecAttr* d_ra;
|
|
chkdb(g_scan_op->getValue(no++, a_addr) != 0);
|
|
chkdb((b_ra = g_scan_op->getValue(no++, b_addr)) != 0);
|
|
chkdb((c_ra = g_scan_op->getValue(no++, c_addr)) != 0);
|
|
chkdb((d_ra = g_scan_op->getValue(no++, d_addr)) != 0);
|
|
chkdb(g_con->execute(NdbTransaction::NoCommit) == 0);
|
|
uint count = 0;
|
|
uint i;
|
|
for (i = 0; i < g_opts.rows; i++)
|
|
g_keys[i].count = 0;
|
|
while (1) {
|
|
int ret;
|
|
a = ~(Uint32)0;
|
|
chkdb((ret = g_scan_op->nextResult()) == 0 || ret == 1);
|
|
if (ret == 1)
|
|
break;
|
|
assert(b_ra->isNULL() == 0 && c_ra->isNULL() != -1 && d_ra->isNULL() != -1);
|
|
val.c_null = c_ra->isNULL();
|
|
val.d_null = d_ra->isNULL();
|
|
i = (uint)a;
|
|
chkrc(i < g_opts.rows);
|
|
Key& key = g_keys[i];
|
|
if (g_opts.loaddata)
|
|
chkrc(key.val.cmp(val) == 0);
|
|
else
|
|
key.val = val;
|
|
key.count++;
|
|
count++;
|
|
}
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_scan_op = 0;
|
|
for (i = 0; i < g_opts.rows; i++)
|
|
chkrc(g_keys[i].count == 1);
|
|
assert(count == g_opts.rows);
|
|
int level = g_opts.loaddata ? 1 : 0;
|
|
llx(level, g_tabname << ": scanned " << g_opts.rows << " rows");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loaddata()
|
|
{
|
|
if (g_opts.loaddata) {
|
|
chkrc(allockeys() == 0);
|
|
makekeys();
|
|
chkrc(insertdata() == 0);
|
|
} else {
|
|
chkrc(countrows() == 0);
|
|
chkrc(g_opts.rows != 0);
|
|
ll0(g_tabname << ": using old table of " << g_opts.rows << " rows");
|
|
chkrc(allockeys() == 0);
|
|
}
|
|
chkrc(scandata() == 0);
|
|
uint i;
|
|
for (i = 0; i < g_opts.rows; i++)
|
|
ll3(i << ": " << g_keys[i]);
|
|
return 0;
|
|
}
|
|
|
|
// true = match, index = match or next higher
|
|
static bool
|
|
sortval(const Val& val, int& index)
|
|
{
|
|
if (unlikely(g_sortcount == 0)) {
|
|
index = 0;
|
|
return false;
|
|
}
|
|
int lo = -1;
|
|
int hi = (int)g_sortcount;
|
|
int ret;
|
|
int j;
|
|
do {
|
|
j = (hi + lo) / 2;
|
|
ret = val.cmp(g_sortkeys[j].val);
|
|
if (ret < 0)
|
|
hi = j;
|
|
else if (ret > 0)
|
|
lo = j;
|
|
else
|
|
break;
|
|
} while (hi - lo > 1);
|
|
if (ret == 0) {
|
|
index = j;
|
|
return true;
|
|
}
|
|
index = hi;
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
sortkeys()
|
|
{
|
|
// insert sort with binary search
|
|
g_sortcount = 0;
|
|
uint i;
|
|
for (i = 0; i < g_opts.rows; i++) {
|
|
const Val& val = g_keys[i].val;
|
|
int index;
|
|
bool match = sortval(val, index);
|
|
Key& dst = g_sortkeys[index];
|
|
if (match) {
|
|
dst.rpk++;
|
|
} else {
|
|
uint bytes = ((int)g_sortcount - index) * sizeof(Key);
|
|
memmove(&dst + 1, &dst, bytes);
|
|
dst.val = val;
|
|
dst.rpk = 1;
|
|
g_sortcount++;
|
|
}
|
|
}
|
|
g_minkey = &g_sortkeys[0];
|
|
g_maxkey = &g_sortkeys[g_sortcount - 1];
|
|
ll1("counted " << g_sortcount << " distinct keys");
|
|
}
|
|
|
|
struct Bnd {
|
|
Val val;
|
|
/*
|
|
* A bound is a partial key value (0 to g_numattrs attributes).
|
|
* It is not equal to any key value. Instead, it has a "side".
|
|
*
|
|
* side = 0 if the bound is empty
|
|
* side = -1 if the bound is "just before" its value
|
|
* side = +1 if the bound is "just after" its value
|
|
*
|
|
* This is another way of looking at strictness of non-empty
|
|
* start and end keys in a range.
|
|
*
|
|
* start key is strict if side = +1
|
|
* end key is strict if side = -1
|
|
*
|
|
* NDB API specifies strictness in the bound type of the last
|
|
* index attribute which is part of the start/end key.
|
|
*
|
|
* LE (0) - strict: n - side: -1
|
|
* LT (1) - strict: y - side: +1
|
|
* GE (2) - strict: n - side: +1
|
|
* GT (3) - strict: y - side: -1
|
|
*
|
|
* A non-empty bound divides keys into 2 disjoint subsets:
|
|
* keys before (cmp() == -1) and keys after (cmp() == +1).
|
|
*/
|
|
int side;
|
|
Bnd& make(uint minattrs);
|
|
Bnd& make(uint minattrs, const Val& theval);
|
|
int cmp(const Val& val) const;
|
|
int type(uint lohi, uint colno) const; // for setBound
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Bnd& bnd)
|
|
{
|
|
out << bnd.val;
|
|
out << " side: " << bnd.side;
|
|
return out;
|
|
}
|
|
|
|
Bnd&
|
|
Bnd::make(uint minattrs)
|
|
{
|
|
uint numattrs = minattrs + urandom(g_numattrs - minattrs);
|
|
val.make(numattrs, false);
|
|
side = val.numattrs == 0 ? 0 : urandom(2) == 0 ? -1 : +1;
|
|
return *this;
|
|
}
|
|
|
|
Bnd&
|
|
Bnd::make(uint minattrs, const Val& theval)
|
|
{
|
|
uint numattrs = minattrs + urandom(g_numattrs - minattrs);
|
|
val = theval;
|
|
val.numattrs = numattrs;
|
|
side = val.numattrs == 0 ? 0 : urandom(2) == 0 ? -1 : +1;
|
|
return *this;
|
|
}
|
|
|
|
int
|
|
Bnd::cmp(const Val& theval) const
|
|
{
|
|
int place; // debug
|
|
int ret;
|
|
do {
|
|
assert(theval.numattrs == g_numattrs);
|
|
int k = theval.cmp(val, val.numattrs);
|
|
if (k != 0) {
|
|
place = 1;
|
|
ret = k;
|
|
break;
|
|
}
|
|
if (side != 0) {
|
|
place = 2;
|
|
ret = -side;
|
|
break;
|
|
}
|
|
place = 3;
|
|
ret = 0;
|
|
assert(val.numattrs == 0);
|
|
} while (0);
|
|
ll3("cmp: val: " << theval << " bnd: " << *this <<
|
|
" return: " << ret << " at " << place);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
Bnd::type(uint lohi, uint colno) const
|
|
{
|
|
int t;
|
|
assert(lohi <= 1 && colno < val.numattrs && (side == -1 || side == +1));
|
|
if (lohi == 0) {
|
|
if (colno + 1 < val.numattrs)
|
|
t = 0; // LE
|
|
else if (side == -1)
|
|
t = 0; // LE
|
|
else
|
|
t = 1; // LT
|
|
} else {
|
|
if (colno + 1 < val.numattrs)
|
|
t = 2; // GE
|
|
else if (side == +1)
|
|
t = 2; // GE
|
|
else
|
|
t = 3; // GT
|
|
}
|
|
return t;
|
|
}
|
|
|
|
struct Range {
|
|
Bnd bnd[2];
|
|
uint minattrs() const;
|
|
uint maxattrs() const;
|
|
int cmp(const Val& val) const; // -1,0,+1 = key is before,in,after range
|
|
uint rowcount() const;
|
|
bool iseq() const;
|
|
// stats
|
|
bool flag;
|
|
uint statrows;
|
|
uint scanrows;
|
|
double errpct;
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Range& range)
|
|
{
|
|
out << "bnd0: " << range.bnd[0] << " bnd1: " << range.bnd[1];
|
|
return out;
|
|
}
|
|
|
|
uint
|
|
Range::minattrs() const
|
|
{
|
|
return min(bnd[0].val.numattrs, bnd[1].val.numattrs);
|
|
}
|
|
|
|
uint
|
|
Range::maxattrs() const
|
|
{
|
|
return max(bnd[0].val.numattrs, bnd[1].val.numattrs);
|
|
}
|
|
|
|
int
|
|
Range::cmp(const Val& theval) const
|
|
{
|
|
int place; // debug
|
|
int ret;
|
|
do {
|
|
int k;
|
|
k = bnd[0].cmp(theval);
|
|
if (k < 0) {
|
|
place = 1;
|
|
ret = -1;
|
|
break;
|
|
}
|
|
k = bnd[1].cmp(theval);
|
|
if (k > 0) {
|
|
place = 2;
|
|
ret = +1;
|
|
break;
|
|
}
|
|
place = 3;
|
|
ret = 0;
|
|
} while (0);
|
|
ll3("cmp: val: " << theval << " range: " << *this <<
|
|
" return: " << ret << " at " << place);
|
|
return ret;
|
|
}
|
|
|
|
uint
|
|
Range::rowcount() const
|
|
{
|
|
ll2("rowcount: " << *this);
|
|
int i;
|
|
// binary search for first and last in range
|
|
int lim[2];
|
|
for (i = 0; i <= 1; i++) {
|
|
ll3("search i=" << i);
|
|
int lo = -1;
|
|
int hi = (int)g_sortcount;
|
|
int ret;
|
|
int j;
|
|
do {
|
|
j = (hi + lo) / 2;
|
|
ret = cmp(g_sortkeys[j].val);
|
|
if (i == 0) {
|
|
if (ret < 0)
|
|
lo = j;
|
|
else
|
|
hi = j;
|
|
} else {
|
|
if (ret > 0)
|
|
hi = j;
|
|
else
|
|
lo = j;
|
|
}
|
|
} while (hi - lo > 1);
|
|
if (ret == 0)
|
|
lim[i] = j;
|
|
else if (i == 0)
|
|
lim[i] = hi;
|
|
else
|
|
lim[i] = lo;
|
|
}
|
|
// the range
|
|
const int lo = max(lim[0], 0);
|
|
const int hi = min(lim[1], (int)g_sortcount - 1);
|
|
if (! g_opts.nochecks) {
|
|
int curr = -1;
|
|
for (i = 0; i < (int)g_sortcount; i++) {
|
|
int k = cmp(g_sortkeys[i].val);
|
|
if (k < 0)
|
|
assert(i < lo);
|
|
else if (k == 0)
|
|
assert(lo <= i && i <= hi);
|
|
else
|
|
assert(i > hi);
|
|
assert(curr <= k);
|
|
if (curr < k)
|
|
curr = k;
|
|
}
|
|
}
|
|
// sum them up
|
|
uint count = 0;
|
|
for (i = lo; i <= hi; i++)
|
|
count += g_sortkeys[i].count;
|
|
ll2("count: " << count << " index lim: " << lim[0] << " " << lim[1]);
|
|
return count;
|
|
}
|
|
|
|
bool
|
|
Range::iseq() const
|
|
{
|
|
return
|
|
minattrs() == maxattrs() &&
|
|
bnd[0].val.cmp(bnd[1].val, minattrs()) == 0 &&
|
|
bnd[0].side < bnd[1].side;
|
|
}
|
|
|
|
static Range* g_ranges = 0;
|
|
|
|
static void
|
|
freeranges()
|
|
{
|
|
if (g_ranges != 0)
|
|
my_free((char*)g_ranges, MYF(0));
|
|
g_ranges = 0;
|
|
}
|
|
|
|
static int
|
|
allocranges()
|
|
{
|
|
freeranges();
|
|
size_t sz = sizeof(Range) * g_opts.ops;
|
|
g_ranges = (Range*)my_malloc(sz, MYF(0));
|
|
chkrc(g_ranges != 0);
|
|
memset(g_ranges, 0x1f, sz);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
makeranges()
|
|
{
|
|
uint i;
|
|
for (i = 0; i < g_opts.ops; i++) {
|
|
Range& range = g_ranges[i];
|
|
range.flag = false; // mark for dup generation done
|
|
bool fulleq = (urandom(100) < g_opts.eqscans);
|
|
bool eq = fulleq || (urandom(100) < g_opts.eqscans);
|
|
bool matcheq = eq && (urandom(10) != 0);
|
|
if (! eq) {
|
|
// random but prefer non-empty and no more than scanpct
|
|
do {
|
|
range.bnd[0].make(0);
|
|
range.bnd[1].make(0);
|
|
uint count = range.rowcount();
|
|
if (count != 0 && 100 * count <= g_opts.scanpct * g_opts.rows)
|
|
break;
|
|
} while (urandom(g_opts.tryhard) != 0);
|
|
} else {
|
|
uint minattrs = fulleq ? g_numattrs : 1;
|
|
if (! matcheq) {
|
|
range.bnd[0].make(minattrs);
|
|
} else {
|
|
uint m = urandom(g_sortcount);
|
|
const Val& val = g_sortkeys[m].val;
|
|
range.bnd[0].make(minattrs, val);
|
|
}
|
|
range.bnd[1] = range.bnd[0];
|
|
range.bnd[0].side = -1;
|
|
range.bnd[1].side = +1;
|
|
// fix types
|
|
range.bnd[0];
|
|
range.bnd[1];
|
|
assert(range.iseq());
|
|
}
|
|
}
|
|
for (i = 0; i < g_opts.ops; i++) {
|
|
Range& range = g_ranges[i];
|
|
if (range.flag)
|
|
continue;
|
|
range.flag = true;
|
|
if (urandom(100) < g_opts.dupscans) {
|
|
uint j = urandom(g_opts.ops);
|
|
do {
|
|
Range& dst = g_ranges[j];
|
|
if (! dst.flag) {
|
|
dst.bnd[0] = range.bnd[0];
|
|
dst.bnd[1] = range.bnd[1];
|
|
dst.flag = true;
|
|
break;
|
|
}
|
|
} while (urandom(g_opts.tryhard) != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
setbounds(const Range& range)
|
|
{
|
|
// currently must do each attr in order
|
|
ll2("setbounds: " << range);
|
|
uint i;
|
|
const Bnd (&bnd)[2] = range.bnd;
|
|
for (i = 0; i < g_numattrs; i++) {
|
|
const Uint32 no = i; // index attribute number
|
|
uint j;
|
|
int type[2] = { -1, -1 };
|
|
for (j = 0; j <= 1; j++) {
|
|
if (no < bnd[j].val.numattrs)
|
|
type[j] = bnd[j].type(j, no);
|
|
}
|
|
for (j = 0; j <= 1; j++) {
|
|
int t = type[j];
|
|
if (t == -1)
|
|
continue;
|
|
if (no + 1 < bnd[j].val.numattrs)
|
|
t &= ~(uint)1; // strict bit is set on last bound only
|
|
const Val& val = bnd[j].val;
|
|
const void* addr = 0;
|
|
if (no == 0)
|
|
addr = (const void*)&val.b;
|
|
else if (no == 1)
|
|
addr = ! val.c_null ? (const void*)val.c : 0;
|
|
else if (no == 2)
|
|
addr = ! val.d_null ? (const void*)&val.d : 0;
|
|
else
|
|
assert(false);
|
|
ll2("setBound attr:" << no << " type:" << t << " val: " << val);
|
|
chkdb(g_rangescan_op->setBound(no, t, addr) == 0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
allocstat()
|
|
{
|
|
g_stat = new NdbIndexStat(g_ind);
|
|
chkdb(g_stat->alloc_cache(32) == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
runstat(Range& range, int flags)
|
|
{
|
|
ll2("runstat: " << range << " flags=" << flags);
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
chkdb((g_rangescan_op = g_con->getNdbIndexScanOperation(g_ind, g_tab)) != 0);
|
|
chkdb(g_rangescan_op->readTuples(NdbOperation::LM_CommittedRead) == 0);
|
|
chkrc(setbounds(range) == 0);
|
|
Uint64 count = ~(Uint64)0;
|
|
chkdb(g_stat->records_in_range(g_ind, g_rangescan_op, g_opts.rows, &count, flags) == 0);
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_rangescan_op = 0;
|
|
range.statrows = (uint)count;
|
|
chkrc((Uint64)range.statrows == count);
|
|
ll2("stat: " << range.statrows);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
runscan(Range& range)
|
|
{
|
|
ll2("runscan: " << range);
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
chkdb((g_rangescan_op = g_con->getNdbIndexScanOperation(g_ind, g_tab)) != 0);
|
|
chkdb(g_rangescan_op->readTuples() == 0);
|
|
chkrc(setbounds(range) == 0);
|
|
Uint32 a;
|
|
char* a_addr = (char*)&a;
|
|
Uint32 no = 0;
|
|
chkdb(g_rangescan_op->getValue(no++, a_addr) != 0);
|
|
chkdb(g_con->execute(NdbTransaction::NoCommit) == 0);
|
|
uint count = 0;
|
|
uint i;
|
|
for (i = 0; i < g_opts.rows; i++)
|
|
g_keys[i].count = 0;
|
|
while (1) {
|
|
int ret;
|
|
a = ~(Uint32)0;
|
|
chkdb((ret = g_rangescan_op->nextResult()) == 0 || ret == 1);
|
|
if (ret == 1)
|
|
break;
|
|
i = (uint)a;
|
|
chkrc(i < g_opts.rows);
|
|
Key& key = g_keys[i];
|
|
ll3("scan: " << key);
|
|
int k = range.cmp(key.val);
|
|
chkrc(k == 0);
|
|
chkrc(key.count == 0);
|
|
key.count++;
|
|
count++;
|
|
}
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_rangescan_op = 0;
|
|
if (! g_opts.nochecks) {
|
|
for (i = 0; i < g_opts.rows; i++) {
|
|
const Key& key = g_keys[i];
|
|
int k = range.cmp(key.val);
|
|
assert((k != 0 && key.count == 0) || (k == 0 && key.count == 1));
|
|
}
|
|
assert(range.rowcount() == count);
|
|
}
|
|
range.scanrows = count;
|
|
ll2("scan: " << range.scanrows);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
runscans()
|
|
{
|
|
uint i;
|
|
for (i = 0; i < g_opts.ops; i++) {
|
|
Range& range = g_ranges[i];
|
|
ll1("range " << i << ": " << range);
|
|
// simulate old handler code
|
|
int flags = 0;
|
|
if (i < 32 || i % 20 == 0)
|
|
flags |= NdbIndexStat::RR_UseDb;
|
|
chkrc(runstat(range, flags) == 0);
|
|
chkrc(runscan(range) == 0);
|
|
// if stat is 0 then it is exact scan count
|
|
chkrc(range.statrows != 0 || range.scanrows == 0);
|
|
// measure error as fraction of total rows
|
|
double x = (double)range.statrows;
|
|
double y = (double)range.scanrows;
|
|
double z = (double)g_opts.rows;
|
|
double err = (x - y) / z;
|
|
// report in pct
|
|
range.errpct = 100.0 * err;
|
|
ll1("range " << i << ":" <<
|
|
" stat: " << range.statrows << " scan: " << range.scanrows <<
|
|
" errpct: " << range.errpct);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct Stat {
|
|
const char* name;
|
|
uint count;
|
|
double sum;
|
|
double minval;
|
|
double maxval;
|
|
double avg;
|
|
double varsum;
|
|
double var;
|
|
double stddev;
|
|
void init();
|
|
void add(const Stat& stat);
|
|
};
|
|
|
|
void
|
|
Stat::init()
|
|
{
|
|
name = "stat";
|
|
count = 0;
|
|
sum = minval = maxval = avg = varsum = var = stddev = 0.0;
|
|
}
|
|
|
|
void
|
|
Stat::add(const Stat& stat)
|
|
{
|
|
if (count == 0) {
|
|
*this = stat;
|
|
return;
|
|
}
|
|
Stat tmp = *this;
|
|
tmp.count = count + stat.count;
|
|
tmp.sum = sum + stat.sum;
|
|
tmp.minval = minval <= stat.minval ? minval : stat.minval;
|
|
tmp.maxval = maxval >= stat.maxval ? maxval : stat.maxval;
|
|
tmp.avg = tmp.sum / double(tmp.count);
|
|
tmp.varsum = varsum + stat.varsum;
|
|
tmp.var = tmp.varsum / double(tmp.count);
|
|
tmp.stddev = sqrt(tmp.var);
|
|
*this = tmp;
|
|
}
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Stat& stat)
|
|
{
|
|
out << stat.name << ": " << "count: " << stat.count
|
|
<< " min: " << stat.minval << " max: " << stat.maxval
|
|
<< " avg: " << stat.avg << " stddev: " << stat.stddev;
|
|
return out;
|
|
}
|
|
|
|
template <class T, class V>
|
|
static void
|
|
computestat(Stat& stat)
|
|
{
|
|
stat.init();
|
|
stat.name = V::name();
|
|
const T* array = V::array();
|
|
stat.count = V::count();
|
|
assert(stat.count != 0);
|
|
uint i;
|
|
for (i = 0; i < stat.count; i++) {
|
|
const T& item = array[i];
|
|
double data = V::data(item);
|
|
stat.sum += data;
|
|
if (i == 0)
|
|
stat.minval = stat.maxval = data;
|
|
else {
|
|
if (stat.minval > data)
|
|
stat.minval = data;
|
|
if (stat.maxval < data)
|
|
stat.maxval = data;
|
|
}
|
|
}
|
|
stat.avg = stat.sum / double(stat.count);
|
|
stat.varsum = 0.0;
|
|
for (i = 0; i < stat.count; i++) {
|
|
const T& item = array[i];
|
|
double data = V::data(item);
|
|
double x = data - stat.avg;
|
|
stat.varsum += x * x;
|
|
}
|
|
stat.var = stat.varsum / double(stat.count);
|
|
stat.stddev = sqrt(stat.var);
|
|
}
|
|
|
|
struct V_rpk {
|
|
static const char* name() { return "rec per key"; }
|
|
static const Key* array() { return g_sortkeys; }
|
|
static uint count() { return g_sortcount; }
|
|
static double data(const Key& key) { return (double)key.rpk; }
|
|
};
|
|
|
|
struct V_rir {
|
|
static const char* name() { return "rir err pct"; }
|
|
static const Range* array() { return g_ranges; }
|
|
static uint count() { return g_opts.ops; }
|
|
static double data(const Range& range) { return (double)range.errpct; }
|
|
};
|
|
|
|
template static void computestat<Key, V_rpk>(Stat& stat);
|
|
template static void computestat<Range, V_rir>(Stat& stat);
|
|
|
|
static Stat g_stat_rpk; // summaries over loops
|
|
static Stat g_stat_rir;
|
|
|
|
static void
|
|
loopstats()
|
|
{
|
|
Stat stat_rpk; // records per key
|
|
Stat stat_rir; // record in range
|
|
if (g_loop == 0) {
|
|
g_stat_rpk.init();
|
|
g_stat_rir.init();
|
|
}
|
|
computestat<Key, V_rpk>(stat_rpk);
|
|
computestat<Range, V_rir>(stat_rir);
|
|
if (g_opts.loop != 1) {
|
|
ll0("=== loop " << g_loop << " summary ===");
|
|
ll0(stat_rpk);
|
|
ll0(stat_rir);
|
|
}
|
|
// accumulate
|
|
g_stat_rpk.add(stat_rpk);
|
|
g_stat_rir.add(stat_rir);
|
|
}
|
|
|
|
static void
|
|
finalstats()
|
|
{
|
|
ll0("=== summary ===");
|
|
ll0(g_stat_rpk);
|
|
ll0(g_stat_rir);
|
|
}
|
|
|
|
static void
|
|
setseed(int n)
|
|
{
|
|
uint seed;
|
|
if (n == -1) {
|
|
if (g_opts.seed == 0)
|
|
return;
|
|
if (g_opts.seed != -1)
|
|
seed = (uint)g_opts.seed;
|
|
else
|
|
seed = 1 + (ushort)getpid();
|
|
} else {
|
|
if (g_opts.seed != 0)
|
|
return;
|
|
seed = n;
|
|
}
|
|
ll0("seed=" << seed);
|
|
srandom(seed);
|
|
}
|
|
|
|
static int
|
|
runtest()
|
|
{
|
|
setseed(-1);
|
|
g_cs = get_charset_by_name(g_csname, MYF(0));
|
|
if (g_cs == 0)
|
|
g_cs = get_charset_by_csname(g_csname, MY_CS_PRIMARY, MYF(0));
|
|
chkrc(g_cs != 0);
|
|
for (g_loop = 0; g_opts.loop == 0 || g_loop < g_opts.loop; g_loop++) {
|
|
ll0("=== loop " << g_loop << " ===");
|
|
setseed(g_loop);
|
|
chkrc(createtable() == 0);
|
|
chkrc(loaddata() == 0);
|
|
sortkeys();
|
|
chkrc(allocranges() == 0);
|
|
makeranges();
|
|
chkrc(allocstat() == 0);
|
|
chkrc(runscans() == 0);
|
|
chkrc(droptable() == 0);
|
|
loopstats();
|
|
}
|
|
finalstats();
|
|
return 0;
|
|
}
|
|
|
|
NDB_STD_OPTS_VARS;
|
|
|
|
static struct my_option
|
|
my_long_options[] =
|
|
{
|
|
NDB_STD_OPTS("testIndexStat"),
|
|
{ "loglevel", 1001, "Logging level in this program 0-3 (default 0)",
|
|
(gptr*)&g_opts.loglevel, (gptr*)&g_opts.loglevel, 0,
|
|
GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
|
{ "seed", 1002, "Random seed (0=loop number, default -1=random)",
|
|
(gptr*)&g_opts.seed, (gptr*)&g_opts.seed, 0,
|
|
GET_INT, REQUIRED_ARG, -1, 0, 0, 0, 0, 0 },
|
|
{ "loop", 1003, "Number of test loops (default 1, 0=forever)",
|
|
(gptr*)&g_opts.loop, (gptr*)&g_opts.loop, 0,
|
|
GET_INT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0 },
|
|
{ "rows", 1004, "Number of rows (default 100000)",
|
|
(gptr*)&g_opts.rows, (gptr*)&g_opts.rows, 0,
|
|
GET_UINT, REQUIRED_ARG, 100000, 0, 0, 0, 0, 0 },
|
|
{ "ops", 1005, "Number of index scans per loop (default 1000)",
|
|
(gptr*)&g_opts.ops, (gptr*)&g_opts.ops, 0,
|
|
GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0 },
|
|
{ "dupkeys", 1006, "Pct records per key (min 100, default 1000)",
|
|
(gptr*)&g_opts.dupkeys, (gptr*)&g_opts.dupkeys, 0,
|
|
GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0 },
|
|
{ "scanpct", 1007, "Preferred max pct of total rows per scan (default 5)",
|
|
(gptr*)&g_opts.scanpct, (gptr*)&g_opts.scanpct, 0,
|
|
GET_UINT, REQUIRED_ARG, 5, 0, 0, 0, 0, 0 },
|
|
{ "nullkeys", 1008, "Pct nulls in each key attribute (default 10)",
|
|
(gptr*)&g_opts.nullkeys, (gptr*)&g_opts.nullkeys, 0,
|
|
GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
|
|
{ "eqscans", 1009, "Pct scans for partial/full equality (default 50)",
|
|
(gptr*)&g_opts.eqscans, (gptr*)&g_opts.eqscans, 0,
|
|
GET_UINT, REQUIRED_ARG, 50, 0, 0, 0, 0, 0 },
|
|
{ "dupscans", 1010, "Pct scans using same bounds (default 10)",
|
|
(gptr*)&g_opts.dupscans, (gptr*)&g_opts.dupscans, 0,
|
|
GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
|
|
{ "keeptable", 1011, "Use existing table and data if any and do not drop",
|
|
(gptr*)&g_opts.keeptable, (gptr*)&g_opts.keeptable, 0,
|
|
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
|
{ "no-extra-checks", 1012, "Omit expensive consistency checks",
|
|
(gptr*)&g_opts.nochecks, (gptr*)&g_opts.nochecks, 0,
|
|
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
|
{ "abort-on-error", 1013, "Dump core on any error",
|
|
(gptr*)&g_opts.abort, (gptr*)&g_opts.abort, 0,
|
|
GET_BOOL, NO_ARG, 0, 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()
|
|
{
|
|
ndbout
|
|
<< g_progname
|
|
<< ": measure records_in_range error as percentage of total rows" << endl;
|
|
my_print_help(my_long_options);
|
|
}
|
|
|
|
static int
|
|
checkoptions()
|
|
{
|
|
chkrc(g_opts.rows != 0);
|
|
chkrc(g_opts.nullkeys <= 100);
|
|
chkrc(g_opts.dupkeys >= 100);
|
|
chkrc(g_opts.scanpct <= 100);
|
|
chkrc(g_opts.eqscans <= 100);
|
|
chkrc(g_opts.dupscans <= 100);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
doconnect()
|
|
{
|
|
g_ncc = new Ndb_cluster_connection();
|
|
chkdb(g_ncc->connect(30) == 0);
|
|
g_ndb = new Ndb(g_ncc, "TEST_DB");
|
|
chkdb(g_ndb->init() == 0 && g_ndb->waitUntilReady(30) == 0);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
freeall()
|
|
{
|
|
delete g_stat;
|
|
freekeys();
|
|
freeranges();
|
|
delete g_ndb;
|
|
delete g_ncc;
|
|
}
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
ndb_init();
|
|
const char* g_progname =
|
|
strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
|
|
uint i;
|
|
ndbout << g_progname;
|
|
for (i = 1; i < argc; i++)
|
|
ndbout << " " << argv[i];
|
|
ndbout << endl;
|
|
int ret;
|
|
ret = handle_options(&argc, &argv, my_long_options, ndb_std_get_one_option);
|
|
if (ret != 0 || argc != 0)
|
|
return NDBT_ProgramExit(NDBT_WRONGARGS);
|
|
if (checkoptions() == 0 && doconnect() == 0 && runtest() == 0) {
|
|
freeall();
|
|
return NDBT_ProgramExit(NDBT_OK);
|
|
}
|
|
freeall();
|
|
return NDBT_ProgramExit(NDBT_FAILED);
|
|
}
|