2369 lines
56 KiB
C++
2369 lines
56 KiB
C++
/*
|
|
Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <algorithm>
|
|
|
|
#include <ndb_global.h>
|
|
#include "m_ctype.h"
|
|
#include <ndb_opts.h>
|
|
#include <NdbApi.hpp>
|
|
#include <NdbIndexStat.hpp>
|
|
#include <NdbTest.hpp>
|
|
#include <ndb_version.h>
|
|
#include <NDBT_Stats.hpp>
|
|
#include <math.h>
|
|
#include <NdbHost.h>
|
|
|
|
struct Opts {
|
|
int loglevel;
|
|
uint seed;
|
|
uint attrs;
|
|
uint loops;
|
|
uint rows;
|
|
uint ops;
|
|
uint nullkeys;
|
|
uint rpk;
|
|
uint rpkvar;
|
|
uint scanpct;
|
|
uint eqscans;
|
|
bool keeptable;
|
|
bool abort;
|
|
const char* dump;
|
|
Opts() :
|
|
loglevel(0),
|
|
seed(0),
|
|
attrs(3),
|
|
loops(1),
|
|
rows(10000),
|
|
ops(100),
|
|
nullkeys(10),
|
|
rpk(10),
|
|
rpkvar(10),
|
|
scanpct(10),
|
|
eqscans(30),
|
|
keeptable(false),
|
|
abort(false),
|
|
dump(0)
|
|
{}
|
|
};
|
|
|
|
static Opts g_opts;
|
|
static uint g_loop = 0;
|
|
|
|
static const char* g_tabname = "ts1";
|
|
static const char* g_indname = "ts1x1";
|
|
static const uint g_numattrs = 3;
|
|
static const uint g_charlen = 10;
|
|
static const char* g_csname = "latin1_swedish_ci";
|
|
static CHARSET_INFO* g_cs;
|
|
|
|
// keys nullability
|
|
static const bool g_b_nullable = true;
|
|
static const bool g_c_nullable = true;
|
|
static const bool g_d_nullable = true;
|
|
|
|
// value limits
|
|
struct Lim {
|
|
bool all_nullable;
|
|
uint b_min;
|
|
uint b_max;
|
|
const char* c_char;
|
|
uint d_min;
|
|
uint d_max;
|
|
};
|
|
|
|
static Lim g_lim_val;
|
|
static Lim g_lim_bnd;
|
|
|
|
static Ndb_cluster_connection* g_ncc = 0;
|
|
static Ndb* g_ndb = 0;
|
|
static Ndb* g_ndb_sys = 0;
|
|
static NdbDictionary::Dictionary* g_dic = 0;
|
|
static const NdbDictionary::Table* g_tab = 0;
|
|
static const NdbDictionary::Index* g_ind = 0;
|
|
static const NdbRecord* g_tab_rec = 0;
|
|
static const NdbRecord* g_ind_rec = 0;
|
|
|
|
struct my_record
|
|
{
|
|
Uint8 m_null_bm;
|
|
Uint8 fill[3];
|
|
Uint32 m_a;
|
|
Uint32 m_b;
|
|
char m_c[1+g_charlen];
|
|
Uint16 m_d;
|
|
};
|
|
|
|
static const Uint32 g_ndbrec_a_offset=offsetof(my_record, m_a);
|
|
static const Uint32 g_ndbrec_b_offset=offsetof(my_record, m_b);
|
|
static const Uint32 g_ndbrec_b_nb_offset=1;
|
|
static const Uint32 g_ndbrec_c_offset=offsetof(my_record, m_c);
|
|
static const Uint32 g_ndbrec_c_nb_offset=2;
|
|
static const Uint32 g_ndbrec_d_offset=offsetof(my_record, m_d);
|
|
static const Uint32 g_ndbrec_d_nb_offset=3;
|
|
|
|
static NdbTransaction* g_con = 0;
|
|
static NdbOperation* g_op = 0;
|
|
static NdbScanOperation* g_scan_op = 0;
|
|
static NdbIndexScanOperation* g_rangescan_op = 0;
|
|
|
|
static NdbIndexStat* g_is = 0;
|
|
static bool g_has_created_stat_tables = false;
|
|
static bool g_has_created_stat_events = false;
|
|
|
|
static uint
|
|
urandom(uint m)
|
|
{
|
|
if (m == 0)
|
|
return 0;
|
|
uint r = (uint)rand();
|
|
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 chker(x) \
|
|
do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; ndbout << "errno: " << errno; 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 chkrc_break(x) \
|
|
if (unlikely(!(x))) { ndbout << "line " << __LINE__ << " FAIL " << #x << endl; break; }
|
|
|
|
#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;
|
|
if (g_ncc != 0) {
|
|
NdbError e;
|
|
e.code = g_ncc->get_latest_error();
|
|
e.message = g_ncc->get_latest_error_msg();
|
|
if (e.code != 0)
|
|
ll0(++any << " ncc: error" << e);
|
|
}
|
|
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_is != 0) {
|
|
const NdbIndexStat::Error& e = g_is->getNdbError();
|
|
if (e.code != 0)
|
|
ll0(++any << " stat: error " << e);
|
|
}
|
|
if (! any)
|
|
ll0("unknown db error");
|
|
}
|
|
|
|
/* Methods to create NdbRecord structs for the table and index */
|
|
static int
|
|
createNdbRecords()
|
|
{
|
|
ll1("createNdbRecords");
|
|
const Uint32 numCols=4;
|
|
const Uint32 numIndexCols=3;
|
|
NdbDictionary::RecordSpecification recSpec[numCols];
|
|
|
|
recSpec[0].column= g_tab->getColumn("a"); // 4 bytes
|
|
recSpec[0].offset= g_ndbrec_a_offset;
|
|
recSpec[0].nullbit_byte_offset= ~(Uint32)0;
|
|
recSpec[0].nullbit_bit_in_byte= ~(Uint32)0;
|
|
|
|
recSpec[1].column= g_tab->getColumn("b"); // 4 bytes
|
|
recSpec[1].offset= g_ndbrec_b_offset;
|
|
if (g_b_nullable) {
|
|
recSpec[1].nullbit_byte_offset= 0;
|
|
recSpec[1].nullbit_bit_in_byte= g_ndbrec_b_nb_offset;
|
|
} else {
|
|
recSpec[1].nullbit_byte_offset= ~(Uint32)0;
|
|
recSpec[1].nullbit_bit_in_byte= ~(Uint32)0;
|
|
}
|
|
|
|
recSpec[2].column= g_tab->getColumn("c"); // Varchar(10) -> ~12 bytes
|
|
recSpec[2].offset= g_ndbrec_c_offset;
|
|
if (g_c_nullable) {
|
|
recSpec[2].nullbit_byte_offset= 0;
|
|
recSpec[2].nullbit_bit_in_byte= g_ndbrec_c_nb_offset;
|
|
} else {
|
|
recSpec[2].nullbit_byte_offset= ~(Uint32)0;
|
|
recSpec[2].nullbit_bit_in_byte= ~(Uint32)0;
|
|
}
|
|
|
|
recSpec[3].column= g_tab->getColumn("d"); // 2 bytes
|
|
recSpec[3].offset= g_ndbrec_d_offset;
|
|
if (g_d_nullable) {
|
|
recSpec[3].nullbit_byte_offset= 0;
|
|
recSpec[3].nullbit_bit_in_byte= g_ndbrec_d_nb_offset;
|
|
} else {
|
|
recSpec[3].nullbit_byte_offset= ~(Uint32)0;
|
|
recSpec[3].nullbit_bit_in_byte= ~(Uint32)0;
|
|
}
|
|
|
|
g_dic = g_ndb->getDictionary();
|
|
g_tab_rec= g_dic->createRecord(g_tab,
|
|
&recSpec[0],
|
|
numCols,
|
|
sizeof(NdbDictionary::RecordSpecification),
|
|
0);
|
|
|
|
chkdb(g_tab_rec != NULL);
|
|
|
|
g_ind_rec= g_dic->createRecord(g_ind,
|
|
&recSpec[1],
|
|
numIndexCols,
|
|
sizeof(NdbDictionary::RecordSpecification),
|
|
0);
|
|
|
|
chkdb(g_ind_rec != NULL);
|
|
g_dic = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// create table ts0 (
|
|
// a int unsigned,
|
|
// b int unsigned, c varchar(10), d smallint unsigned,
|
|
// primary key using hash (a), index (b, c, d) )
|
|
|
|
static int
|
|
createtable()
|
|
{
|
|
ll1("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::Unsigned);
|
|
col.setNullable(g_b_nullable);
|
|
tab.addColumn(col);
|
|
}
|
|
{
|
|
NdbDictionary::Column col("c");
|
|
col.setType(NdbDictionary::Column::Varchar);
|
|
col.setLength(g_charlen);
|
|
col.setCharset(g_cs);
|
|
col.setNullable(g_c_nullable);
|
|
tab.addColumn(col);
|
|
}
|
|
{
|
|
NdbDictionary::Column col("d");
|
|
col.setType(NdbDictionary::Column::Smallunsigned);
|
|
col.setNullable(g_d_nullable);
|
|
tab.addColumn(col);
|
|
}
|
|
|
|
g_dic = g_ndb->getDictionary();
|
|
if (g_dic->getTable(g_tabname) != 0)
|
|
chkdb(g_dic->dropTable(g_tabname) == 0);
|
|
chkdb(g_dic->createTable(tab) == 0);
|
|
chkdb((g_tab = g_dic->getTable(g_tabname)) != 0);
|
|
g_dic = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
createindex()
|
|
{
|
|
ll1("createindex");
|
|
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();
|
|
chkdb(g_dic->createIndex(ind) == 0);
|
|
chkdb((g_ind = g_dic->getIndex(g_indname, g_tabname)) != 0);
|
|
g_dic = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
droptable()
|
|
{
|
|
ll1("droptable");
|
|
g_dic = g_ndb->getDictionary();
|
|
chkdb(g_dic->dropTable(g_tabname) == 0);
|
|
g_dic = 0;
|
|
return 0;
|
|
}
|
|
|
|
// values for keys and bounds
|
|
|
|
struct Val {
|
|
uint8 m_numattrs;
|
|
int8 b_null;
|
|
int8 c_null;
|
|
int8 d_null;
|
|
Uint32 b;
|
|
uchar c[1 + g_charlen];
|
|
Uint16 d;
|
|
Val();
|
|
void init();
|
|
void copy(const Val& val2);
|
|
void make(uint numattrs, const Lim& lim);
|
|
int cmp(const Val& val2, uint numattrs = g_numattrs, uint* num_eq = 0) const;
|
|
void fromib(const NdbIndexScanOperation::IndexBound& ib, uint j);
|
|
|
|
private:
|
|
Val& operator=(const Val&);
|
|
Val(const Val&);
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Val& val)
|
|
{
|
|
out << "[";
|
|
if (val.m_numattrs >= 1) {
|
|
if (val.b_null)
|
|
out << "NULL";
|
|
else
|
|
out << val.b;
|
|
}
|
|
if (val.m_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.m_numattrs >= 3) {
|
|
out << " ";
|
|
if (val.d_null)
|
|
out <<" NULL";
|
|
else
|
|
out << val.d;
|
|
}
|
|
out << "]";
|
|
return out;
|
|
}
|
|
|
|
Val::Val()
|
|
{
|
|
init();
|
|
}
|
|
|
|
void
|
|
Val::init()
|
|
{
|
|
m_numattrs = 0;
|
|
// junk rest
|
|
b_null = -1;
|
|
c_null = -1;
|
|
d_null = -1;
|
|
b = ~(Uint32)0;
|
|
memset(c, 0xff, sizeof(c));
|
|
d = ~(Uint16)0;
|
|
}
|
|
|
|
void
|
|
Val::copy(const Val& val2)
|
|
{
|
|
require(this != &val2);
|
|
init();
|
|
m_numattrs = val2.m_numattrs;
|
|
if (m_numattrs >= 1) {
|
|
require(val2.b_null == 0 || val2.b_null == 1);
|
|
b_null = val2.b_null;
|
|
if (!b_null)
|
|
b = val2.b;
|
|
}
|
|
if (m_numattrs >= 2) {
|
|
require(val2.c_null == 0 || val2.c_null == 1);
|
|
c_null = val2.c_null;
|
|
if (!c_null)
|
|
memcpy(c, val2.c, sizeof(c));
|
|
}
|
|
if (m_numattrs >= 3) {
|
|
require(val2.d_null == 0 || val2.d_null == 1);
|
|
d_null = val2.d_null;
|
|
if (!d_null)
|
|
d = val2.d;
|
|
}
|
|
}
|
|
|
|
void
|
|
Val::make(uint numattrs, const Lim& lim)
|
|
{
|
|
require(numattrs <= g_numattrs);
|
|
if (numattrs >= 1) {
|
|
const bool nullable = g_b_nullable || lim.all_nullable;
|
|
if (nullable && urandom(100) < g_opts.nullkeys)
|
|
b_null = 1;
|
|
else {
|
|
require(lim.b_min <= lim.b_max);
|
|
b = lim.b_min + urandom(lim.b_max - lim.b_min + 1);
|
|
b_null = 0;
|
|
}
|
|
}
|
|
if (numattrs >= 2) {
|
|
const bool nullable = g_c_nullable || lim.all_nullable;
|
|
if (nullable && urandom(100) < g_opts.nullkeys)
|
|
c_null = 1;
|
|
else {
|
|
// prefer shorter
|
|
const uint len = urandom(urandom(g_charlen + 1) + 1);
|
|
c[0] = len;
|
|
for (uint j = 0; j < len; j++) {
|
|
uint k = urandom((uint)strlen(lim.c_char));
|
|
c[1 + j] = lim.c_char[k];
|
|
}
|
|
c_null = 0;
|
|
}
|
|
}
|
|
if (numattrs >= 3) {
|
|
const bool nullable = g_d_nullable || lim.all_nullable;
|
|
if (nullable && urandom(100) < g_opts.nullkeys)
|
|
d_null = 1;
|
|
else {
|
|
require(lim.d_min <= lim.d_max);
|
|
d = lim.d_min + urandom(lim.d_max - lim.d_min + 1);
|
|
d_null = 0;
|
|
}
|
|
}
|
|
m_numattrs = numattrs;
|
|
}
|
|
|
|
int
|
|
Val::cmp(const Val& val2, uint numattrs, uint* num_eq) const
|
|
{
|
|
require(numattrs <= m_numattrs);
|
|
require(numattrs <= val2.m_numattrs);
|
|
uint n = 0; // attr index where differs
|
|
uint k = 0;
|
|
if (k == 0 && numattrs >= 1) {
|
|
if (! b_null && ! val2.b_null) {
|
|
if (b < val2.b)
|
|
k = -1;
|
|
else if (b > val2.b)
|
|
k = +1;
|
|
} else if (! b_null) {
|
|
k = +1;
|
|
} else if (! val2.b_null) {
|
|
k = -1;
|
|
}
|
|
if (k == 0)
|
|
n++;
|
|
}
|
|
if (k == 0 && numattrs >= 2) {
|
|
if (! c_null && ! val2.c_null) {
|
|
const uchar* s1 = &c[1];
|
|
const uchar* s2 = &val2.c[1];
|
|
const uint l1 = (uint)c[0];
|
|
const uint l2 = (uint)val2.c[0];
|
|
require(l1 <= g_charlen && l2 <= g_charlen);
|
|
k = g_cs->coll->strnncollsp(g_cs, s1, l1, s2, l2);
|
|
} else if (! c_null) {
|
|
k = +1;
|
|
} else if (! val2.c_null) {
|
|
k = -1;
|
|
}
|
|
if (k == 0)
|
|
n++;
|
|
}
|
|
if (k == 0 && numattrs >= 3) {
|
|
if (! d_null && ! val2.d_null) {
|
|
if (d < val2.d)
|
|
k = -1;
|
|
else if (d > val2.d)
|
|
k = +1;
|
|
} else if (! d_null) {
|
|
k = +1;
|
|
} else if (! val2.d_null) {
|
|
k = -1;
|
|
}
|
|
if (k == 0)
|
|
n++;
|
|
}
|
|
require(n <= numattrs);
|
|
if (num_eq != 0)
|
|
*num_eq = n;
|
|
return k;
|
|
}
|
|
|
|
void
|
|
Val::fromib(const NdbIndexScanOperation::IndexBound& ib, uint j)
|
|
{
|
|
const char* key = (j == 0 ? ib.low_key : ib.high_key);
|
|
const uint numattrs = (j == 0 ? ib.low_key_count : ib.high_key_count);
|
|
const Uint8 nullbits = *(const Uint8*)key;
|
|
require(numattrs <= g_numattrs);
|
|
if (numattrs >= 1) {
|
|
if (nullbits & (1 << g_ndbrec_b_nb_offset))
|
|
b_null = 1;
|
|
else {
|
|
memcpy(&b, &key[g_ndbrec_b_offset], sizeof(b));
|
|
b_null = 0;
|
|
}
|
|
}
|
|
if (numattrs >= 2) {
|
|
if (nullbits & (1 << g_ndbrec_c_nb_offset))
|
|
c_null = 1;
|
|
else {
|
|
memcpy(c, &key[g_ndbrec_c_offset], sizeof(c));
|
|
c_null = 0;
|
|
}
|
|
}
|
|
if (numattrs >= 3) {
|
|
if (nullbits & (1 << g_ndbrec_d_nb_offset))
|
|
d_null = 1;
|
|
else {
|
|
memcpy(&d, &key[g_ndbrec_d_offset], sizeof(d));
|
|
d_null = 0;
|
|
}
|
|
}
|
|
m_numattrs = numattrs;
|
|
}
|
|
|
|
// index keys
|
|
|
|
struct Key {
|
|
Val m_val;
|
|
int8 m_flag; // temp use
|
|
Key();
|
|
|
|
private:
|
|
Key& operator=(const Key&);
|
|
Key(const Key&);
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Key& key)
|
|
{
|
|
out << key.m_val;
|
|
if (key.m_flag != -1)
|
|
out << " flag: " << key.m_flag;
|
|
return out;
|
|
}
|
|
|
|
Key::Key()
|
|
{
|
|
m_flag = -1;
|
|
}
|
|
|
|
static Key* g_keys = 0;
|
|
static uint* g_sortkeys = 0;
|
|
|
|
static void
|
|
freekeys()
|
|
{
|
|
delete [] g_keys;
|
|
delete [] g_sortkeys;
|
|
g_keys = 0;
|
|
g_sortkeys = 0;
|
|
}
|
|
|
|
static void
|
|
allockeys()
|
|
{
|
|
freekeys();
|
|
g_keys = new Key [g_opts.rows];
|
|
g_sortkeys = new uint [g_opts.rows];
|
|
require(g_keys != 0 && g_sortkeys != 0);
|
|
memset(g_sortkeys, 0xff, sizeof(uint) * g_opts.rows);
|
|
}
|
|
|
|
static int
|
|
cmpkeys(const void* p1, const void* p2)
|
|
{
|
|
const uint i1 = *(const uint*)p1;
|
|
const uint i2 = *(const uint*)p2;
|
|
require(i1 < g_opts.rows && i2 < g_opts.rows);
|
|
const Key& key1 = g_keys[i1];
|
|
const Key& key2 = g_keys[i2];
|
|
const int k = key1.m_val.cmp(key2.m_val, g_opts.attrs);
|
|
return k;
|
|
}
|
|
|
|
static void
|
|
sortkeys()
|
|
{
|
|
ll2("sortkeys");
|
|
uint i;
|
|
|
|
// sort
|
|
for (i = 0; i < g_opts.rows; i++)
|
|
g_sortkeys[i] = i;
|
|
qsort(g_sortkeys, g_opts.rows, sizeof(uint), cmpkeys);
|
|
|
|
// verify
|
|
uint unique = 1;
|
|
for (i = 1; i < g_opts.rows; i++) {
|
|
const uint i1 = g_sortkeys[i - 1];
|
|
const uint i2 = g_sortkeys[i];
|
|
require(i1 < g_opts.rows && i2 < g_opts.rows);
|
|
const Key& key1 = g_keys[i1];
|
|
const Key& key2 = g_keys[i2];
|
|
const int k = key1.m_val.cmp(key2.m_val, g_opts.attrs);
|
|
require(k <= 0);
|
|
if (k < 0)
|
|
unique++;
|
|
}
|
|
|
|
// show min max key
|
|
ll1("minkey:" << g_keys[g_sortkeys[0]]);
|
|
ll1("maxkey:" << g_keys[g_sortkeys[g_opts.rows - 1]]);
|
|
ll1("unique:" << unique);
|
|
}
|
|
|
|
static void
|
|
makekeys()
|
|
{
|
|
ll1("makekeys");
|
|
|
|
uint initrows = g_opts.rows / g_opts.rpk;
|
|
require(initrows != 0);
|
|
|
|
// distinct keys
|
|
uint i = 0;
|
|
while (i < initrows) {
|
|
Key& key = g_keys[i];
|
|
key.m_val.make(g_numattrs, g_lim_val);
|
|
i++;
|
|
}
|
|
|
|
// remaining keys
|
|
while (i < g_opts.rows) {
|
|
// if rpkvar is 10, multiply rpk by number between 0.1 and 10.0
|
|
double a = (double)(1 + urandom(g_opts.rpkvar * g_opts.rpkvar));
|
|
double b = a / (double)g_opts.rpkvar;
|
|
double c = b * (double)g_opts.rpk;
|
|
const uint n = (uint)(c + 0.5);
|
|
// select random key to duplicate from initrows
|
|
const uint k = urandom(initrows);
|
|
uint j = 0;
|
|
while (i < g_opts.rows && j < n) {
|
|
g_keys[i].m_val.copy(g_keys[k].m_val);
|
|
j++;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// shuffle
|
|
i = 0;
|
|
while (i < g_opts.rows) {
|
|
uint j = urandom(g_opts.rows);
|
|
if (i != j) {
|
|
Key tmp;
|
|
tmp.m_val.copy(g_keys[i].m_val);
|
|
g_keys[i].m_val.copy(g_keys[j].m_val);
|
|
g_keys[j].m_val.copy(tmp.m_val);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
// sort
|
|
sortkeys();
|
|
}
|
|
|
|
// data loading
|
|
|
|
static int
|
|
verifydata()
|
|
{
|
|
ll3("verifydata");
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
chkdb((g_scan_op = g_con->getNdbScanOperation(g_tab)) != 0);
|
|
chkdb(g_scan_op->readTuples(NdbScanOperation::LM_CommittedRead) == 0);
|
|
Uint32 a;
|
|
Val val;
|
|
val.m_numattrs = g_numattrs;
|
|
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++) {
|
|
Key& key = g_keys[i];
|
|
key.m_flag = false; // not scanned
|
|
}
|
|
while (1) {
|
|
int ret;
|
|
a = ~(Uint32)0;
|
|
chkdb((ret = g_scan_op->nextResult()) == 0 || ret == 1);
|
|
if (ret == 1)
|
|
break;
|
|
val.b_null = b_ra->isNULL();
|
|
val.c_null = c_ra->isNULL();
|
|
val.d_null = d_ra->isNULL();
|
|
require(val.b_null == 0 || (g_b_nullable && val.b_null == 1));
|
|
require(val.c_null == 0 || (g_c_nullable && val.c_null == 1));
|
|
require(val.d_null == 0 || (g_d_nullable && val.d_null == 1));
|
|
i = (uint)a;
|
|
chkrc(i < g_opts.rows);
|
|
Key& key = g_keys[i];
|
|
chkrc(key.m_val.cmp(val) == 0);
|
|
chkrc(key.m_flag == false);
|
|
key.m_flag = true;
|
|
count++;
|
|
}
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_scan_op = 0;
|
|
for (i = 0; i < g_opts.rows; i++) {
|
|
Key& key = g_keys[i];
|
|
chkrc(key.m_flag == true);
|
|
key.m_flag = -1; // forget
|
|
}
|
|
require(count == g_opts.rows);
|
|
ll3("verifydata: " << g_opts.rows << " rows");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loaddata(bool update)
|
|
{
|
|
ll1("loaddata: update: " << update);
|
|
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);
|
|
if (!update)
|
|
chkdb(g_op->insertTuple() == 0);
|
|
else
|
|
chkdb(g_op->updateTuple() == 0);
|
|
Uint32 a = i;
|
|
const Val& val = g_keys[i].m_val;
|
|
const char* a_addr = (const char*)&a;
|
|
const char* b_addr = ! val.b_null ? (const char*)&val.b : 0;
|
|
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;
|
|
|
|
// check data and cmp routines
|
|
chkrc(verifydata() == 0);
|
|
|
|
for (uint i = 0; i < g_opts.rows; i++)
|
|
ll3("load " << i << ": " << g_keys[i]);
|
|
ll0("loaddata: " << g_opts.rows << " rows");
|
|
return 0;
|
|
}
|
|
|
|
// bounds
|
|
|
|
struct Bnd {
|
|
Val m_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).
|
|
*/
|
|
int8 m_side;
|
|
int8 m_lohi; // 0-lo 1-hi as part of Rng
|
|
Bnd();
|
|
bool isempty() const;
|
|
void copy(const Bnd& bnd2); // does not copy m_lohi
|
|
Bnd& make(uint minattrs);
|
|
Bnd& make(uint minattrs, const Val& theval);
|
|
int cmp(const Key& key) const;
|
|
int cmp(const Bnd& bnd2);
|
|
int type(uint colno) const; // for setBound
|
|
void fromib(const NdbIndexScanOperation::IndexBound& ib, uint j);
|
|
|
|
private:
|
|
Bnd& operator=(const Bnd&);
|
|
Bnd(const Bnd&);
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Bnd& bnd)
|
|
{
|
|
if (bnd.m_lohi == 0)
|
|
out << "L";
|
|
else if (bnd.m_lohi == 1)
|
|
out << "H";
|
|
else
|
|
out << bnd.m_lohi << "?";
|
|
out << bnd.m_val;
|
|
if (bnd.m_side == 0)
|
|
;
|
|
else if (bnd.m_side == -1)
|
|
out << "-";
|
|
else if (bnd.m_side == +1)
|
|
out << "+";
|
|
return out;
|
|
}
|
|
|
|
Bnd::Bnd()
|
|
{
|
|
m_side = 0;
|
|
m_lohi = -1;
|
|
}
|
|
|
|
bool
|
|
Bnd::isempty() const
|
|
{
|
|
return m_val.m_numattrs == 0;
|
|
}
|
|
|
|
void
|
|
Bnd::copy(const Bnd& bnd2)
|
|
{
|
|
m_val.copy(bnd2.m_val);
|
|
m_side = bnd2.m_side;
|
|
}
|
|
|
|
Bnd&
|
|
Bnd::make(uint minattrs)
|
|
{
|
|
require(minattrs <= g_opts.attrs);
|
|
require(m_lohi == 0 || m_lohi == 1);
|
|
uint numattrs = minattrs + urandom(g_numattrs - minattrs + 1);
|
|
m_val.make(numattrs, g_lim_bnd);
|
|
m_side = m_val.m_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);
|
|
m_val.copy(theval);
|
|
m_val.m_numattrs = numattrs;
|
|
m_side = m_val.m_numattrs == 0 ? 0 : urandom(2) == 0 ? -1 : +1;
|
|
return *this;
|
|
}
|
|
|
|
int
|
|
Bnd::cmp(const Key& key) const
|
|
{
|
|
int place; // debug
|
|
int ret;
|
|
do {
|
|
int k = key.m_val.cmp(m_val, m_val.m_numattrs);
|
|
if (k != 0) {
|
|
place = 1;
|
|
ret = k;
|
|
break;
|
|
}
|
|
if (m_side != 0) {
|
|
place = 2;
|
|
ret = (-1) * m_side;
|
|
break;
|
|
}
|
|
place = 3;
|
|
ret = 0;
|
|
require(m_val.m_numattrs == 0);
|
|
} while (0);
|
|
ll3("bnd: " << *this << " cmp key: " << key
|
|
<< " ret: " << ret << " place: " << place);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
Bnd::cmp(const Bnd& bnd2)
|
|
{
|
|
int place; // debug
|
|
int ret;
|
|
const Bnd& bnd1 = *this;
|
|
const Val& val1 = bnd1.m_val;
|
|
const Val& val2 = bnd2.m_val;
|
|
const uint numattrs1 = val1.m_numattrs;
|
|
const uint numattrs2 = val2.m_numattrs;
|
|
const uint n = (numattrs1 < numattrs2 ? numattrs1 : numattrs2);
|
|
do {
|
|
int k = val1.cmp(val2, n);
|
|
if (k != 0) {
|
|
place = 1;
|
|
ret = k;
|
|
break;
|
|
}
|
|
if (numattrs1 < numattrs2) {
|
|
place = 2;
|
|
ret = (+1) * bnd1.m_side;
|
|
break;
|
|
}
|
|
if (numattrs1 > numattrs2) {
|
|
place = 3;
|
|
ret = (-1) * bnd1.m_side;
|
|
break;
|
|
}
|
|
if (bnd1.m_side < bnd2.m_side) {
|
|
place = 4;
|
|
ret = -1;
|
|
break;
|
|
}
|
|
if (bnd1.m_side > bnd2.m_side) {
|
|
place = 5;
|
|
ret = +1;
|
|
break;
|
|
}
|
|
place = 6;
|
|
ret = 0;
|
|
} while (0);
|
|
ll3("bnd: " << *this << " cmp bnd: " << bnd2
|
|
<< " ret: " << ret << " place: " << place);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
Bnd::type(uint colno) const
|
|
{
|
|
int t;
|
|
require(colno < m_val.m_numattrs && (m_side == -1 || m_side == +1));
|
|
require(m_lohi == 0 || m_lohi == 1);
|
|
if (m_lohi == 0) {
|
|
if (colno + 1 < m_val.m_numattrs)
|
|
t = 0; // LE
|
|
else if (m_side == -1)
|
|
t = 0; // LE
|
|
else
|
|
t = 1; // LT
|
|
} else {
|
|
if (colno + 1 < m_val.m_numattrs)
|
|
t = 2; // GE
|
|
else if (m_side == +1)
|
|
t = 2; // GE
|
|
else
|
|
t = 3; // GT
|
|
}
|
|
return t;
|
|
}
|
|
|
|
void
|
|
Bnd::fromib(const NdbIndexScanOperation::IndexBound& ib, uint j)
|
|
{
|
|
Val& val = m_val;
|
|
val.fromib(ib, j);
|
|
const uint numattrs = (j == 0 ? ib.low_key_count : ib.high_key_count);
|
|
const bool inclusive = (j == 0 ? ib.low_inclusive : ib.high_inclusive);
|
|
if (numattrs == 0) {
|
|
m_side = 0;
|
|
} else {
|
|
m_side = (j == 0 ? (inclusive ? -1 : +1) : (inclusive ? +1 : -1));
|
|
}
|
|
m_lohi = j;
|
|
}
|
|
|
|
// stats values
|
|
|
|
struct Stval {
|
|
Uint32 rir_v2;
|
|
double rir;
|
|
double rpk[g_numattrs];
|
|
bool empty;
|
|
char rule[NdbIndexStat::RuleBufferBytes];
|
|
Stval();
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Stval& st)
|
|
{
|
|
out << "rir_v2: " << st.rir_v2;
|
|
out << " rir_v4: " << st.rir;
|
|
out << " rpk:[ ";
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
if (k != 0)
|
|
out << " ";
|
|
out << st.rpk[k];
|
|
}
|
|
out << " ]";
|
|
out << " " << (st.empty ? "E" : "N");
|
|
out << " " << st.rule;
|
|
return out;
|
|
}
|
|
|
|
Stval::Stval()
|
|
{
|
|
rir_v2 = 0;
|
|
rir = 0.0;
|
|
for (uint k = 0; k < g_numattrs; k++)
|
|
rpk[k] = 0.0;
|
|
empty = false;
|
|
strcpy(rule, "-");
|
|
}
|
|
|
|
// ranges
|
|
|
|
struct Rng {
|
|
Bnd m_bnd[2];
|
|
Int32 m_rowcount;
|
|
// stats v2
|
|
double errpct;
|
|
// stats v4
|
|
Stval m_st_scan; // exact stats computed from keys in range
|
|
Stval m_st_stat; // interpolated kernel stats via g_is
|
|
Rng();
|
|
uint minattrs() const;
|
|
uint maxattrs() const;
|
|
bool iseq() const;
|
|
bool isempty() const;
|
|
void copy(const Rng& rng2);
|
|
int cmp(const Key& key) const; // -1,0,+1 = key is before,in,after range
|
|
uint rowcount() const;
|
|
void fromib(const NdbIndexScanOperation::IndexBound& ib);
|
|
|
|
private:
|
|
Rng& operator=(const Rng&);
|
|
Rng(const Rng&);
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Rng& rng)
|
|
{
|
|
out << rng.m_bnd[0] << " " << rng.m_bnd[1];
|
|
if (rng.m_rowcount != -1)
|
|
out << " rows: " << rng.m_rowcount;
|
|
return out;
|
|
}
|
|
|
|
Rng::Rng()
|
|
{
|
|
m_bnd[0].m_lohi = 0;
|
|
m_bnd[1].m_lohi = 1;
|
|
m_rowcount = -1;
|
|
}
|
|
|
|
uint
|
|
Rng::minattrs() const
|
|
{
|
|
return std::min(m_bnd[0].m_val.m_numattrs, m_bnd[1].m_val.m_numattrs);
|
|
}
|
|
|
|
uint
|
|
Rng::maxattrs() const
|
|
{
|
|
return std::max(m_bnd[0].m_val.m_numattrs, m_bnd[1].m_val.m_numattrs);
|
|
}
|
|
|
|
bool
|
|
Rng::iseq() const
|
|
{
|
|
return
|
|
minattrs() == maxattrs() &&
|
|
m_bnd[0].m_val.cmp(m_bnd[1].m_val, minattrs()) == 0 &&
|
|
m_bnd[0].m_side < m_bnd[1].m_side;
|
|
}
|
|
|
|
bool
|
|
Rng::isempty() const
|
|
{
|
|
return m_bnd[0].isempty() && m_bnd[1].isempty();
|
|
}
|
|
|
|
void
|
|
Rng::copy(const Rng& rng2)
|
|
{
|
|
m_bnd[0].copy(rng2.m_bnd[0]);
|
|
m_bnd[1].copy(rng2.m_bnd[1]);
|
|
m_rowcount = rng2.m_rowcount;
|
|
}
|
|
|
|
int
|
|
Rng::cmp(const Key& key) const
|
|
{
|
|
int place; // debug
|
|
int ret;
|
|
do {
|
|
int k;
|
|
k = m_bnd[0].cmp(key);
|
|
if (k < 0) {
|
|
place = 1;
|
|
ret = -1;
|
|
break;
|
|
}
|
|
k = m_bnd[1].cmp(key);
|
|
if (k > 0) {
|
|
place = 2;
|
|
ret = +1;
|
|
break;
|
|
}
|
|
place = 3;
|
|
ret = 0;
|
|
} while (0);
|
|
ll3("rng: " << *this << " cmp key: " << key
|
|
<< " ret: " << ret << " place: " << place);
|
|
return ret;
|
|
}
|
|
|
|
uint
|
|
Rng::rowcount() const
|
|
{
|
|
ll3("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_opts.rows;
|
|
int ret;
|
|
int j;
|
|
do {
|
|
j = (hi + lo) / 2;
|
|
require(lo < j && j < hi);
|
|
ret = cmp(g_keys[g_sortkeys[j]]);
|
|
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;
|
|
}
|
|
|
|
// verify is expensive due to makeranges() multiple tries
|
|
const bool verify = (urandom(10) == 0);
|
|
const int lo = std::max(lim[0], 0);
|
|
const int hi = std::min(lim[1], (int)g_opts.rows - 1);
|
|
if (verify) {
|
|
int pos = -1; // before, within, after
|
|
for (i = 0; i < (int)g_opts.rows; i++) {
|
|
int k = cmp(g_keys[g_sortkeys[i]]);
|
|
if (k < 0)
|
|
require(i < lo);
|
|
else if (k == 0)
|
|
require(lo <= i && i <= hi);
|
|
else
|
|
require(i > hi);
|
|
require(pos <= k);
|
|
if (pos < k)
|
|
pos = k;
|
|
}
|
|
}
|
|
|
|
// result
|
|
require(hi - lo + 1 >= 0);
|
|
uint count = hi - lo + 1;
|
|
ll3("rowcount: " << count << " lim: " << lim[0] << " " << lim[1]);
|
|
return count;
|
|
}
|
|
|
|
void
|
|
Rng::fromib(const NdbIndexScanOperation::IndexBound& ib)
|
|
{
|
|
for (uint j = 0; j <= 1; j++) {
|
|
Bnd& bnd = m_bnd[j];
|
|
bnd.fromib(ib, j);
|
|
}
|
|
}
|
|
|
|
static Rng* g_rnglist = 0;
|
|
|
|
static void
|
|
freeranges()
|
|
{
|
|
delete [] g_rnglist;
|
|
g_rnglist = 0;
|
|
}
|
|
|
|
static void
|
|
allocranges()
|
|
{
|
|
freeranges();
|
|
g_rnglist = new Rng [g_opts.ops];
|
|
require(g_rnglist != 0);
|
|
}
|
|
|
|
static void
|
|
makeranges()
|
|
{
|
|
ll1("makeranges");
|
|
const uint mintries = 20;
|
|
const uint maxtries = 80;
|
|
const uint fudgefac = 10;
|
|
|
|
for (uint i = 0; i < g_opts.ops; i++) {
|
|
const bool eqpart = (urandom(100) < g_opts.eqscans);
|
|
const bool eqfull = eqpart && (urandom(100) < g_opts.eqscans);
|
|
Rng rng; // candidate
|
|
uint j;
|
|
for (j = 0; j < maxtries; j++) {
|
|
Rng rng2;
|
|
if (!eqpart) {
|
|
rng2.m_bnd[0].make(0);
|
|
rng2.m_bnd[1].make(0);
|
|
} else {
|
|
const uint mincnt = eqfull ? g_opts.attrs : 1;
|
|
rng2.m_bnd[0].make(mincnt);
|
|
rng2.m_bnd[1].copy(rng2.m_bnd[0]);
|
|
rng2.m_bnd[0].m_side = -1;
|
|
rng2.m_bnd[1].m_side = +1;
|
|
require(rng2.iseq());
|
|
}
|
|
rng2.m_rowcount = (Int32)rng2.rowcount();
|
|
// 0-discard 1-replace or accept 2-accept
|
|
int action = 0;
|
|
do {
|
|
// first candidate
|
|
if (rng.m_rowcount == -1) {
|
|
action = 1;
|
|
break;
|
|
}
|
|
require(rng.m_rowcount != -1);
|
|
// prefer some bounds
|
|
if (rng2.isempty()) {
|
|
if (urandom(fudgefac) != 0)
|
|
action = 0;
|
|
else
|
|
action = 1;
|
|
break;
|
|
}
|
|
// prefer some rows
|
|
if (rng2.m_rowcount == 0) {
|
|
action = 0;
|
|
break;
|
|
}
|
|
// accept if row count under given pct
|
|
require((uint)rng2.m_rowcount <= g_opts.rows);
|
|
if (100 * (uint)rng2.m_rowcount <= g_opts.scanpct * g_opts.rows) {
|
|
if (urandom(fudgefac) != 0) {
|
|
action = 2;
|
|
break;
|
|
}
|
|
}
|
|
// replace if less rows
|
|
if (rng2.m_rowcount < rng.m_rowcount) {
|
|
if (urandom(fudgefac) != 0) {
|
|
action = 1;
|
|
break;
|
|
}
|
|
}
|
|
} while (0);
|
|
if (action != 0) {
|
|
rng.copy(rng2);
|
|
if (action == 2 || j >= mintries)
|
|
break;
|
|
}
|
|
}
|
|
g_rnglist[i].copy(rng);
|
|
ll2("rng " << i << ": " << rng << " tries: " << j);
|
|
}
|
|
}
|
|
|
|
// verify ranges via range scans
|
|
|
|
static int
|
|
setbounds(const Rng& rng)
|
|
{
|
|
// currently must do each attr in order
|
|
ll3("setbounds: " << rng);
|
|
uint i;
|
|
const Bnd (&bnd)[2] = rng.m_bnd;
|
|
for (i = 0; i < g_numattrs; i++) {
|
|
const Uint32 no = i; // index attribute number
|
|
uint j;
|
|
int type[2] = { -1, -1 };
|
|
// determine inclusivity (boundtype) of upper+lower bounds on this col.
|
|
// -1 == no bound on the col.
|
|
for (j = 0; j <= 1; j++) {
|
|
if (no < bnd[j].m_val.m_numattrs)
|
|
type[j] = bnd[j].type(no);
|
|
}
|
|
for (j = 0; j <= 1; j++) {
|
|
int t = type[j];
|
|
if (t == -1)
|
|
continue;
|
|
if (no + 1 < bnd[j].m_val.m_numattrs)
|
|
t &= ~(uint)1; // strict bit is set on last bound only
|
|
const Val& val = bnd[j].m_val;
|
|
const void* addr = 0;
|
|
if (no == 0)
|
|
addr = ! val.b_null ? (const void*)&val.b : 0;
|
|
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
|
|
require(false);
|
|
ll3("setBound attr:" << no << " type:" << t << " val: " << val);
|
|
chkdb(g_rangescan_op->setBound(no, t, addr) == 0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
scanrange(const Rng& rng)
|
|
{
|
|
ll3("scanrange: " << rng);
|
|
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(rng) == 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++) {
|
|
Key& key = g_keys[i];
|
|
key.m_flag = false; // not scanned
|
|
}
|
|
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 = rng.cmp(key);
|
|
chkrc(k == 0);
|
|
chkrc(key.m_flag == false);
|
|
key.m_flag = true;
|
|
count++;
|
|
}
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_rangescan_op = 0;
|
|
|
|
for (i = 0; i < g_opts.rows; i++) {
|
|
Key& key = g_keys[i];
|
|
int k = rng.cmp(key);
|
|
if (k != 0) // not in range
|
|
chkrc(key.m_flag == false);
|
|
else
|
|
chkrc(key.m_flag == true);
|
|
key.m_flag = -1; // forget
|
|
}
|
|
require((uint)rng.m_rowcount == count);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
scanranges()
|
|
{
|
|
ll1("scanranges");
|
|
for (uint i = 0; i < g_opts.ops; i++) {
|
|
const Rng& rng = g_rnglist[i];
|
|
chkrc(scanrange(rng) == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// stats v4 update
|
|
|
|
static int
|
|
definestat()
|
|
{
|
|
ll1("definestat");
|
|
require(g_is != 0 && g_ind != 0 && g_tab != 0);
|
|
chkdb(g_is->set_index(*g_ind, *g_tab) == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
updatestat()
|
|
{
|
|
ll1("updatestat");
|
|
if (urandom(2) == 0) {
|
|
g_dic = g_ndb->getDictionary();
|
|
chkdb(g_dic->updateIndexStat(*g_ind, *g_tab) == 0);
|
|
g_dic = 0;
|
|
} else {
|
|
chkdb(g_is->update_stat(g_ndb_sys) == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
readstat()
|
|
{
|
|
ll1("readstat");
|
|
|
|
NdbIndexStat::Head head;
|
|
chkdb(g_is->read_head(g_ndb_sys) == 0);
|
|
g_is->get_head(head);
|
|
chkrc(head.m_found == true);
|
|
chkrc(head.m_sampleVersion != 0);
|
|
ll1("readstat:"
|
|
<< " sampleVersion: " << head.m_sampleVersion
|
|
<< " sampleCount: " << head.m_sampleCount);
|
|
|
|
NdbIndexStat::CacheInfo infoQuery;
|
|
chkdb(g_is->read_stat(g_ndb_sys) == 0);
|
|
g_is->move_cache();
|
|
g_is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
|
|
ll1("readstat: cache bytes: " << infoQuery.m_totalBytes);
|
|
return 0;
|
|
}
|
|
|
|
// test polling after updatestat
|
|
|
|
static int
|
|
startlistener()
|
|
{
|
|
ll1("startlistener");
|
|
chkdb(g_is->create_listener(g_ndb_sys) == 0);
|
|
chkdb(g_is->execute_listener(g_ndb_sys) == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
runlistener()
|
|
{
|
|
ll1("runlistener");
|
|
int ret;
|
|
chkdb((ret = g_is->poll_listener(g_ndb_sys, 10000)) != -1);
|
|
chkrc(ret == 1);
|
|
// one event is expected
|
|
chkdb((ret = g_is->next_listener(g_ndb_sys)) != -1);
|
|
chkrc(ret == 1);
|
|
chkdb((ret = g_is->next_listener(g_ndb_sys)) != -1);
|
|
chkrc(ret == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
stoplistener()
|
|
{
|
|
ll1("stoplistener");
|
|
chkdb(g_is->drop_listener(g_ndb_sys) != -1);
|
|
return 0;
|
|
}
|
|
|
|
// stats queries
|
|
|
|
// exact stats from scan results
|
|
static void
|
|
queryscan(Rng& rng)
|
|
{
|
|
ll3("queryscan");
|
|
|
|
uint rir;
|
|
uint unq[g_numattrs];
|
|
rir = 0;
|
|
for (uint k = 0; k < g_opts.attrs; k++)
|
|
unq[0] = 0;
|
|
Key prevkey;
|
|
for (uint i = 0; i < g_opts.rows; i++) {
|
|
const Key& key = g_keys[g_sortkeys[i]];
|
|
int res = rng.cmp(key);
|
|
if (res != 0)
|
|
continue;
|
|
rir++;
|
|
if (rir == 1) {
|
|
for (uint k = 0; k < g_opts.attrs; k++)
|
|
unq[k] = 1;
|
|
} else {
|
|
uint num_eq = ~0;
|
|
int res = prevkey.m_val.cmp(key.m_val, g_opts.attrs, &num_eq);
|
|
if (res == 0)
|
|
require(num_eq == g_opts.attrs);
|
|
else {
|
|
require(res < 0);
|
|
require(num_eq < g_opts.attrs);
|
|
unq[num_eq]++;
|
|
// propagate down
|
|
for (uint k = num_eq + 1; k < g_opts.attrs; k++)
|
|
unq[k]++;
|
|
}
|
|
}
|
|
prevkey.m_val.copy(key.m_val);
|
|
}
|
|
require(rng.m_rowcount != -1);
|
|
require((uint)rng.m_rowcount == rir);
|
|
|
|
Stval& st = rng.m_st_scan;
|
|
st.rir_v2 = rir;
|
|
st.rir = rir == 0 ? 1.0 : (double)rir;
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
if (rir == 0)
|
|
st.rpk[k] = 1.0;
|
|
else {
|
|
require(rir >= unq[k]);
|
|
require(unq[k] != 0);
|
|
st.rpk[k] = (double)rir / (double)unq[k];
|
|
}
|
|
}
|
|
st.empty = (rir == 0);
|
|
ll2("queryscan: " << st);
|
|
}
|
|
|
|
/* This method initialises the passed in IndexBound
|
|
* to represent the range passed in.
|
|
* It assumes that the storage pointed to by low_key
|
|
* and high_key in the passed IndexBound can be overwritten
|
|
* and is long enough to store the data
|
|
*/
|
|
static int
|
|
initialiseIndexBound(const Rng& rng,
|
|
NdbIndexScanOperation::IndexBound& ib,
|
|
my_record* low_key, my_record* high_key)
|
|
{
|
|
ll3("initialiseIndexBound: " << rng);
|
|
uint i;
|
|
const Bnd (&bnd)[2] = rng.m_bnd;
|
|
Uint32 colsInBound[2]= {0, 0};
|
|
bool boundInclusive[2]= {false, false};
|
|
|
|
memset(&ib, 0xf1, sizeof(ib));
|
|
memset(low_key, 0xf2, sizeof(*low_key));
|
|
memset(high_key, 0xf3, sizeof(*high_key));
|
|
|
|
// Clear nullbit storage
|
|
low_key->m_null_bm = 0;
|
|
high_key->m_null_bm = 0;
|
|
|
|
for (i = 0; i < g_numattrs; i++) {
|
|
const Uint32 no = i; // index attribute number
|
|
uint j;
|
|
int type[2] = { -1, -1 };
|
|
// determine inclusivity (boundtype) of upper+lower bounds on this col.
|
|
// -1 == no bound on the col.
|
|
for (j = 0; j <= 1; j++) {
|
|
if (no < bnd[j].m_val.m_numattrs)
|
|
type[j] = bnd[j].type(no);
|
|
}
|
|
for (j = 0; j <= 1; j++) {
|
|
/* Get ptr to key storage space for this bound */
|
|
my_record* keyBuf= (j==0) ? low_key : high_key;
|
|
int t = type[j];
|
|
if (t == -1)
|
|
continue;
|
|
colsInBound[j]++;
|
|
|
|
if (no + 1 >= bnd[j].m_val.m_numattrs)
|
|
// Last column in bound, inclusive if GE or LE (or EQ)
|
|
// i.e. bottom bit of boundtype is clear
|
|
boundInclusive[j]= !(t & 1);
|
|
|
|
const Val& val = bnd[j].m_val;
|
|
if (no == 0)
|
|
{
|
|
if (! val.b_null)
|
|
keyBuf->m_b= val.b;
|
|
|
|
if (g_b_nullable)
|
|
keyBuf->m_null_bm |= ((val.b_null?1:0) << g_ndbrec_b_nb_offset);
|
|
}
|
|
else if (no == 1)
|
|
{
|
|
if (! val.c_null)
|
|
memcpy(&keyBuf->m_c[0], (const void*)&val.c, 1+ g_charlen);
|
|
|
|
if (g_c_nullable)
|
|
keyBuf->m_null_bm |= ((val.c_null?1:0) << g_ndbrec_c_nb_offset);
|
|
}
|
|
else if (no == 2)
|
|
{
|
|
if (! val.d_null)
|
|
keyBuf->m_d= val.d;
|
|
|
|
if (g_d_nullable)
|
|
keyBuf->m_null_bm |= ((val.d_null?1:0) << g_ndbrec_d_nb_offset);
|
|
}
|
|
else
|
|
require(false);
|
|
ll3("initialiseIndexBound attr:" << no << " type:" << t << " val: " << val);
|
|
}
|
|
}
|
|
|
|
/* Now have everything we need to initialise the IndexBound */
|
|
ib.low_key = (char*)low_key;
|
|
ib.low_key_count= colsInBound[0];
|
|
ib.low_inclusive= boundInclusive[0];
|
|
ib.high_key = (char*)high_key;
|
|
ib.high_key_count= colsInBound[1];
|
|
ib.high_inclusive= boundInclusive[1];
|
|
ib.range_no= 0;
|
|
|
|
ll3(" indexBound low_key_count=" << ib.low_key_count <<
|
|
" low_inc=" << ib.low_inclusive <<
|
|
" high_key_count=" << ib.high_key_count <<
|
|
" high_inc=" << ib.high_inclusive);
|
|
ll3(" low bound b=" << *((Uint32*) &ib.low_key[g_ndbrec_b_offset]) <<
|
|
" d=" << *((Uint16*) &ib.low_key[g_ndbrec_d_offset]) <<
|
|
" first byte=" << ib.low_key[0]);
|
|
ll3(" high bound b=" << *((Uint32*) &ib.high_key[g_ndbrec_b_offset]) <<
|
|
" d=" << *((Uint16*) &ib.high_key[g_ndbrec_d_offset]) <<
|
|
" first byte=" << ib.high_key[0]);
|
|
|
|
// verify by reverse
|
|
{
|
|
Rng rng;
|
|
rng.fromib(ib);
|
|
require(rng.m_bnd[0].cmp(bnd[0]) == 0);
|
|
require(rng.m_bnd[1].cmp(bnd[1]) == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
querystat_v2(Rng& rng)
|
|
{
|
|
ll3("querystat_v2");
|
|
|
|
/* Create IndexBound and key storage space */
|
|
NdbIndexScanOperation::IndexBound ib;
|
|
my_record low_key;
|
|
my_record high_key;
|
|
|
|
chkdb((g_con = g_ndb->startTransaction()) != 0);
|
|
chkrc(initialiseIndexBound(rng, ib, &low_key, &high_key) == 0);
|
|
|
|
Uint64 count = ~(Uint64)0;
|
|
chkdb(g_is->records_in_range(g_ind,
|
|
g_con,
|
|
g_ind_rec,
|
|
g_tab_rec,
|
|
&ib,
|
|
0,
|
|
&count,
|
|
0) == 0);
|
|
g_ndb->closeTransaction(g_con);
|
|
g_con = 0;
|
|
g_rangescan_op = 0;
|
|
|
|
Stval& st = rng.m_st_stat;
|
|
chkrc(count < (1 << 30));
|
|
st.rir_v2 = (Uint32)count;
|
|
ll2("querystat_v2: " << st.rir_v2 << " rows");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
querystat(Rng& rng)
|
|
{
|
|
ll3("querystat");
|
|
|
|
// set up range
|
|
Uint8 bound_lo_buffer[NdbIndexStat::BoundBufferBytes];
|
|
Uint8 bound_hi_buffer[NdbIndexStat::BoundBufferBytes];
|
|
NdbIndexStat::Bound bound_lo(g_is, bound_lo_buffer);
|
|
NdbIndexStat::Bound bound_hi(g_is, bound_hi_buffer);
|
|
NdbIndexStat::Range range(bound_lo, bound_hi);
|
|
|
|
// convert to IndexBound (like in mysqld)
|
|
NdbIndexScanOperation::IndexBound ib;
|
|
my_record low_key;
|
|
my_record high_key;
|
|
chkrc(initialiseIndexBound(rng, ib, &low_key, &high_key) == 0);
|
|
chkrc(g_is->convert_range(range, g_ind_rec, &ib) == 0);
|
|
|
|
// index stat query
|
|
Uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
|
|
NdbIndexStat::Stat stat(stat_buffer);
|
|
chkdb(g_is->query_stat(range, stat) == 0);
|
|
|
|
// save result
|
|
Stval& st = rng.m_st_stat;
|
|
g_is->get_rir(stat, &st.rir);
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
g_is->get_rpk(stat, k, &st.rpk[k]);
|
|
}
|
|
g_is->get_empty(stat, &st.empty);
|
|
g_is->get_rule(stat, st.rule);
|
|
|
|
ll2("querystat: " << st);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
queryranges()
|
|
{
|
|
ll2("queryranges");
|
|
for (uint i = 0; i < g_opts.ops; i++) {
|
|
Rng& rng = g_rnglist[i];
|
|
ll1("rng " << i << ": " << rng);
|
|
// exact stats
|
|
queryscan(rng);
|
|
// interpolated stats
|
|
chkrc(querystat_v2(rng) == 0);
|
|
chkrc(querystat(rng) == 0);
|
|
const Stval& st1 = rng.m_st_scan;
|
|
const Stval& st2 = rng.m_st_stat;
|
|
// if rir v2 is zero then it is exact
|
|
chkrc(st2.rir_v2 != 0 || st1.rir_v2 == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// general statistics methods
|
|
|
|
struct Stats : public NDBT_Stats {
|
|
Stats();
|
|
void add(double x2);
|
|
void add(const Stats& sum2);
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Stats& st)
|
|
{
|
|
out << "count: " << st.getCount()
|
|
<< " min: " << st.getMin()
|
|
<< " max: " << st.getMax()
|
|
<< " mean: " << st.getMean()
|
|
<< " stddev: " << st.getStddev();
|
|
return out;
|
|
}
|
|
|
|
Stats::Stats()
|
|
{
|
|
}
|
|
|
|
void
|
|
Stats::add(double x2)
|
|
{
|
|
addObservation(x2);
|
|
}
|
|
|
|
void
|
|
Stats::add(const Stats& st2)
|
|
{
|
|
*this += st2;
|
|
}
|
|
|
|
// error statistics scan vs stat
|
|
|
|
struct Sterr {
|
|
Stats rir_v2;
|
|
Stats rir;
|
|
Stats rpk[g_numattrs];
|
|
Sterr();
|
|
void add(const Sterr& st2);
|
|
};
|
|
|
|
static NdbOut&
|
|
operator<<(NdbOut& out, const Sterr& st)
|
|
{
|
|
out << "rir_v2: " << st.rir_v2 << endl;
|
|
out << "rir_v4: " << st.rir;
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
out << endl << "rpk[" << k << "]: " << st.rpk[k];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
Sterr::Sterr()
|
|
{
|
|
}
|
|
|
|
void
|
|
Sterr::add(const Sterr& st2)
|
|
{
|
|
rir_v2.add(st2.rir_v2);
|
|
rir.add(st2.rir);
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
rpk[k].add(st2.rpk[k]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sumrange(const Rng& rng, Sterr& st)
|
|
{
|
|
const Stval& st1 = rng.m_st_scan;
|
|
const Stval& st2 = rng.m_st_stat;
|
|
|
|
// rir_v2 error as pct of total rows
|
|
{
|
|
double rows = (double)g_opts.rows;
|
|
double x1 = (double)st1.rir_v2;
|
|
double x2 = (double)st2.rir_v2;
|
|
double x3 = 100.0 * (x2 - x1) / rows;
|
|
st.rir_v2.add(x3);
|
|
}
|
|
|
|
// rir error as pct of total rows
|
|
{
|
|
double rows = (double)g_opts.rows;
|
|
double x1 = st1.rir;
|
|
double x2 = st2.rir;
|
|
double x3 = 100.0 * (x2 - x1) / rows;
|
|
st.rir.add(x3);
|
|
}
|
|
|
|
// rpk errors as plain diff
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
double x1 = st1.rpk[k];
|
|
double x2 = st2.rpk[k];
|
|
double x3 = (x2 - x1);
|
|
st.rpk[k].add(x3);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sumranges(Sterr& st)
|
|
{
|
|
for (uint i = 0; i < g_opts.ops; i++) {
|
|
const Rng& rng = g_rnglist[i];
|
|
sumrange(rng, st);
|
|
}
|
|
}
|
|
|
|
// loop and final stats
|
|
|
|
static Sterr g_sterr;
|
|
|
|
static void
|
|
loopstats()
|
|
{
|
|
Sterr st;
|
|
sumranges(st);
|
|
if (g_opts.loops != 1) {
|
|
ll0("=== loop " << g_loop << " summary ===");
|
|
ll0(st);
|
|
}
|
|
// accumulate
|
|
g_sterr.add(st);
|
|
}
|
|
|
|
static int
|
|
loopdumps()
|
|
{
|
|
char file[200];
|
|
if (g_opts.dump == 0)
|
|
return 0;
|
|
{
|
|
BaseString::snprintf(file, sizeof(file),
|
|
"%s.key.%d", g_opts.dump, g_loop);
|
|
FILE* f = 0;
|
|
chker((f = fopen(file, "w")) != 0);
|
|
fprintf(f, "a");
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
if (k == 0)
|
|
fprintf(f, ",b_null,b");
|
|
else if (k == 1)
|
|
fprintf(f, ",c_null,c");
|
|
else if (k == 2)
|
|
fprintf(f, ",d_null,d");
|
|
else
|
|
require(false);
|
|
}
|
|
fprintf(f, "\n");
|
|
for (uint i = 0; i < g_opts.rows; i++) {
|
|
const Key& key = g_keys[g_sortkeys[i]];
|
|
const Val& val = key.m_val;
|
|
fprintf(f, "%u", i);
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
if (k == 0) {
|
|
fprintf(f, ",%d,", val.b_null);
|
|
if (!val.b_null)
|
|
fprintf(f, "%u", val.b);
|
|
} else if (k == 1) {
|
|
fprintf(f, ",%d,", val.c_null);
|
|
if (!val.c_null)
|
|
fprintf(f, "%.*s", val.c[0], &val.c[1]);
|
|
} else if (k == 2) {
|
|
fprintf(f, ",%d,", val.d_null);
|
|
if (!val.d_null)
|
|
fprintf(f, "%u", val.d);
|
|
} else {
|
|
require(false);
|
|
}
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
chker(fclose(f) == 0);
|
|
}
|
|
{
|
|
BaseString::snprintf(file, sizeof(file),
|
|
"%s.range.%d", g_opts.dump, g_loop);
|
|
FILE* f = 0;
|
|
chker((f = fopen(file, "w")) != 0);
|
|
fprintf(f, "op");
|
|
for (uint j = 0; j <= 1; j++) {
|
|
const char* suf = (j == 0 ? "_lo" : "_hi");
|
|
fprintf(f, ",attrs%s", suf);
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
if (k == 0)
|
|
fprintf(f, ",b_null%s,b%s", suf, suf);
|
|
else if (k == 1)
|
|
fprintf(f, ",c_null%s,c%s", suf, suf);
|
|
else if (k == 2)
|
|
fprintf(f, ",d_null%s,d%s", suf, suf);
|
|
else
|
|
require(false);
|
|
}
|
|
fprintf(f, ",side%s", suf);
|
|
}
|
|
fprintf(f, "\n");
|
|
for (uint i = 0; i < g_opts.ops; i++) {
|
|
const Rng& rng = g_rnglist[i];
|
|
fprintf(f, "%u", i);
|
|
for (uint j = 0; j <= 1; j++) {
|
|
const Bnd& bnd = rng.m_bnd[j];
|
|
const Val& val = bnd.m_val;
|
|
fprintf(f, ",%u", val.m_numattrs);
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
if (k >= val.m_numattrs)
|
|
fprintf(f, ",,");
|
|
else if (k == 0) {
|
|
fprintf(f, ",%d,", val.b_null);
|
|
if (!val.b_null)
|
|
fprintf(f, "%u", val.b);
|
|
} else if (k == 1) {
|
|
fprintf(f, ",%d,", val.c_null);
|
|
if (!val.c_null)
|
|
fprintf(f, "%.*s", val.c[0], &val.c[1]);
|
|
} else if (k == 2) {
|
|
fprintf(f, ",%d,", val.d_null);
|
|
if (!val.d_null)
|
|
fprintf(f, "%u", val.d);
|
|
} else {
|
|
require(false);
|
|
}
|
|
}
|
|
fprintf(f, ",%d", bnd.m_side);
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
chker(fclose(f) == 0);
|
|
}
|
|
{
|
|
BaseString::snprintf(file, sizeof(file),
|
|
"%s.stat.%d", g_opts.dump, g_loop);
|
|
FILE* f = 0;
|
|
chker((f = fopen(file, "w")) != 0);
|
|
fprintf(f, "op");
|
|
for (uint j = 0; j <= 1; j++) {
|
|
const char* suf = (j == 0 ? "_scan" : "_stat");
|
|
fprintf(f, ",rir_v2%s", suf);
|
|
fprintf(f, ",rir%s", suf);
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
fprintf(f, ",rpk_%u%s", k, suf);
|
|
}
|
|
fprintf(f, ",empty%s", suf);
|
|
if (j == 1)
|
|
fprintf(f, ",rule%s", suf);
|
|
}
|
|
fprintf(f, "\n");
|
|
for (uint i = 0; i < g_opts.ops; i++) {
|
|
const Rng& rng = g_rnglist[i];
|
|
fprintf(f, "%u", i);
|
|
for (uint j = 0; j <= 1; j++) {
|
|
const Stval& st = (j == 0 ? rng.m_st_scan : rng.m_st_stat);
|
|
fprintf(f, ",%u", st.rir_v2);
|
|
fprintf(f, ",%.2f", st.rir);
|
|
for (uint k = 0; k < g_opts.attrs; k++) {
|
|
fprintf(f, ",%.2f", st.rpk[k]);
|
|
}
|
|
fprintf(f, ",%d", st.empty);
|
|
if (j == 1)
|
|
fprintf(f, ",%s", st.rule);
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
chker(fclose(f) == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
finalstats()
|
|
{
|
|
ll0("=== summary ===");
|
|
ll0(g_sterr);
|
|
}
|
|
|
|
static int
|
|
runtest()
|
|
{
|
|
ll1("sizeof Val: " << sizeof(Val));
|
|
ll1("sizeof Key: " << sizeof(Key));
|
|
ll1("sizeof Bnd: " << sizeof(Bnd));
|
|
ll1("sizeof Rng: " << sizeof(Rng));
|
|
|
|
uint seed = g_opts.seed;
|
|
if (seed != 1) { // not loop number
|
|
if (seed == 0) { // random
|
|
seed = 2 + NdbHost_GetProcessId();
|
|
}
|
|
ll0("random seed is " << seed);
|
|
srand(seed);
|
|
} else {
|
|
ll0("random seed is " << "loop number");
|
|
}
|
|
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);
|
|
|
|
allockeys();
|
|
allocranges();
|
|
chkrc(createtable() == 0);
|
|
chkrc(createindex() == 0);
|
|
chkrc(createNdbRecords() == 0);
|
|
chkrc(definestat() == 0);
|
|
chkrc(startlistener() == 0);
|
|
|
|
for (g_loop = 0; g_opts.loops == 0 || g_loop < g_opts.loops; g_loop++) {
|
|
ll0("=== loop " << g_loop << " ===");
|
|
uint seed = g_opts.seed;
|
|
if (seed == 1) { // loop number
|
|
seed = g_loop;
|
|
srand(seed);
|
|
}
|
|
makekeys();
|
|
chkrc_break(loaddata(g_loop != 0) == 0);
|
|
makeranges();
|
|
chkrc_break(scanranges() == 0);
|
|
chkrc_break(updatestat() == 0);
|
|
chkrc_break(runlistener() == 0);
|
|
chkrc_break(readstat() == 0);
|
|
chkrc_break(queryranges() == 0);
|
|
loopstats();
|
|
chkrc_break(loopdumps() == 0);
|
|
}
|
|
finalstats();
|
|
|
|
chkrc(stoplistener() == 0);
|
|
if (!g_opts.keeptable)
|
|
chkrc(droptable() == 0);
|
|
freeranges();
|
|
freekeys();
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
doconnect()
|
|
{
|
|
g_ncc = new Ndb_cluster_connection();
|
|
require(g_ncc != 0);
|
|
chkdb(g_ncc->connect(30) == 0);
|
|
g_ndb = new Ndb(g_ncc, "TEST_DB");
|
|
require(g_ndb != 0);
|
|
chkdb(g_ndb->init() == 0 && g_ndb->waitUntilReady(30) == 0);
|
|
g_ndb_sys = new Ndb(g_ncc, "mysql");
|
|
require(g_ndb_sys != 0);
|
|
chkdb(g_ndb_sys->init() == 0 && g_ndb_sys->waitUntilReady(30) == 0);
|
|
g_is = new NdbIndexStat;
|
|
require(g_is != 0);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
dodisconnect()
|
|
{
|
|
delete g_is;
|
|
delete g_ndb_sys;
|
|
delete g_ndb;
|
|
delete g_ncc;
|
|
}
|
|
|
|
static struct my_option
|
|
my_long_options[] =
|
|
{
|
|
NDB_STD_OPTS("testIndexStat"),
|
|
{ "loglevel", NDB_OPT_NOSHORT,
|
|
"Logging level in this program 0-3 (default 0)",
|
|
(uchar **)&g_opts.loglevel, (uchar **)&g_opts.loglevel, 0,
|
|
GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
|
{ "seed", NDB_OPT_NOSHORT, "Random seed (default 0=random, 1=loop number)",
|
|
(uchar **)&g_opts.seed, (uchar **)&g_opts.seed, 0,
|
|
GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
|
{ "loops", NDB_OPT_NOSHORT, "Number of test loops (default 1, 0=forever)",
|
|
(uchar **)&g_opts.loops, (uchar **)&g_opts.loops, 0,
|
|
GET_INT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0 },
|
|
{ "rows", NDB_OPT_NOSHORT, "Number of rows (default 10000)",
|
|
(uchar **)&g_opts.rows, (uchar **)&g_opts.rows, 0,
|
|
GET_UINT, REQUIRED_ARG, 100000, 0, 0, 0, 0, 0 },
|
|
{ "ops", NDB_OPT_NOSHORT,"Number of index scans per loop (default 100)",
|
|
(uchar **)&g_opts.ops, (uchar **)&g_opts.ops, 0,
|
|
GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0 },
|
|
{ "nullkeys", NDB_OPT_NOSHORT, "Pct nulls in each key attribute (default 10)",
|
|
(uchar **)&g_opts.nullkeys, (uchar **)&g_opts.nullkeys, 0,
|
|
GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
|
|
{ "rpk", NDB_OPT_NOSHORT, "Avg records per full key (default 10)",
|
|
(uchar **)&g_opts.rpk, (uchar **)&g_opts.rpk, 0,
|
|
GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
|
|
{ "rpkvar", NDB_OPT_NOSHORT, "Vary rpk by factor (default 10, none 1)",
|
|
(uchar **)&g_opts.rpkvar, (uchar **)&g_opts.rpkvar, 0,
|
|
GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
|
|
{ "scanpct", NDB_OPT_NOSHORT,
|
|
"Preferred max pct of total rows per scan (default 10)",
|
|
(uchar **)&g_opts.scanpct, (uchar **)&g_opts.scanpct, 0,
|
|
GET_UINT, REQUIRED_ARG, 5, 0, 0, 0, 0, 0 },
|
|
{ "eqscans", NDB_OPT_NOSHORT,
|
|
"Pct scans for partial/full equality (default 30)",
|
|
(uchar **)&g_opts.eqscans, (uchar **)&g_opts.eqscans, 0,
|
|
GET_UINT, REQUIRED_ARG, 50, 0, 0, 0, 0, 0 },
|
|
{ "keeptable", NDB_OPT_NOSHORT,
|
|
"Do not drop table at exit",
|
|
(uchar **)&g_opts.keeptable, (uchar **)&g_opts.keeptable, 0,
|
|
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
|
{ "abort", NDB_OPT_NOSHORT, "Dump core on any error",
|
|
(uchar **)&g_opts.abort, (uchar **)&g_opts.abort, 0,
|
|
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
|
{ "dump", NDB_OPT_NOSHORT, "Write CSV files name.* of keys,ranges,stats",
|
|
(uchar **)&g_opts.dump, (uchar **)&g_opts.dump, 0,
|
|
GET_STR, REQUIRED_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
|
|
short_usage_sub()
|
|
{
|
|
ndb_short_usage_sub(NULL);
|
|
}
|
|
|
|
static void
|
|
usage()
|
|
{
|
|
ndbout << my_progname << ": ordered index stats test" << endl;
|
|
}
|
|
|
|
static int
|
|
checkoptions()
|
|
{
|
|
chkrc(g_opts.rows != 0);
|
|
chkrc(g_opts.nullkeys <= 100);
|
|
chkrc(g_opts.rpk != 0);
|
|
g_opts.rpk = std::min(g_opts.rpk, g_opts.rows);
|
|
chkrc(g_opts.rpkvar != 0);
|
|
chkrc(g_opts.scanpct <= 100);
|
|
chkrc(g_opts.eqscans <= 100);
|
|
// set value limits
|
|
g_lim_val.all_nullable = false;
|
|
g_lim_bnd.all_nullable = true;
|
|
g_lim_val.b_min = g_opts.rows;
|
|
g_lim_val.b_max = 2 * g_opts.rows;
|
|
g_lim_bnd.b_min = 90 * g_lim_val.b_min / 100;
|
|
g_lim_bnd.b_max = 110 * g_lim_val.b_max / 100;
|
|
g_lim_val.c_char = "bcd";
|
|
g_lim_bnd.c_char = "abcde";
|
|
g_lim_val.d_min = 100;
|
|
g_lim_val.d_max = 200;
|
|
g_lim_bnd.d_min = 0;
|
|
g_lim_bnd.d_max = 300;
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int
|
|
docreate_stat_tables()
|
|
{
|
|
if (g_is->check_systables(g_ndb_sys) == 0)
|
|
return 0;
|
|
ll1("check_systables: " << g_is->getNdbError());
|
|
|
|
ll0("create stat tables");
|
|
chkdb(g_is->create_systables(g_ndb_sys) == 0);
|
|
g_has_created_stat_tables = true;
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int
|
|
dodrop_stat_tables()
|
|
{
|
|
if (g_has_created_stat_tables == false)
|
|
return 0;
|
|
|
|
ll0("drop stat tables");
|
|
chkdb(g_is->drop_systables(g_ndb_sys) == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
docreate_stat_events()
|
|
{
|
|
if (g_is->check_sysevents(g_ndb_sys) == 0)
|
|
return 0;
|
|
ll1("check_sysevents: " << g_is->getNdbError());
|
|
|
|
ll0("create stat events");
|
|
chkdb(g_is->create_sysevents(g_ndb_sys) == 0);
|
|
g_has_created_stat_events = true;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
dodrop_stat_events()
|
|
{
|
|
if (g_has_created_stat_events == false)
|
|
return 0;
|
|
|
|
ll0("drop stat events");
|
|
chkdb(g_is->drop_sysevents(g_ndb_sys) == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
docreate_sys_objects()
|
|
{
|
|
require(g_is != 0 && g_ndb_sys != 0);
|
|
chkrc(docreate_stat_tables() == 0);
|
|
chkrc(docreate_stat_events() == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
dodrop_sys_objects()
|
|
{
|
|
require(g_is != 0 && g_ndb_sys != 0);
|
|
chkrc(dodrop_stat_events() == 0);
|
|
chkrc(dodrop_stat_tables() == 0);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
ndb_init();
|
|
my_progname = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
|
|
uint i;
|
|
ndbout << my_progname;
|
|
for (i = 1; i < (uint)argc; i++)
|
|
ndbout << " " << argv[i];
|
|
ndbout << endl;
|
|
int ret;
|
|
ndb_opt_set_usage_funcs(short_usage_sub, usage);
|
|
ret = handle_options(&argc, &argv, my_long_options, ndb_std_get_one_option);
|
|
if (ret != 0 || argc != 0) {
|
|
ll0("wrong args");
|
|
return NDBT_ProgramExit(NDBT_WRONGARGS);
|
|
}
|
|
if (checkoptions() == -1) {
|
|
ll0("invalid args");
|
|
return NDBT_ProgramExit(NDBT_WRONGARGS);
|
|
}
|
|
if (doconnect() == -1) {
|
|
ll0("connect failed");
|
|
return NDBT_ProgramExit(NDBT_FAILED);
|
|
}
|
|
if (docreate_sys_objects() == -1) {
|
|
ll0("failed to check or create stat tables and events");
|
|
goto failed;
|
|
}
|
|
if (runtest() == -1) {
|
|
ll0("test failed");
|
|
goto failed;
|
|
}
|
|
if (dodrop_sys_objects() == -1) {
|
|
ll0("failed to drop created stat tables or events");
|
|
goto failed;
|
|
}
|
|
dodisconnect();
|
|
return NDBT_ProgramExit(NDBT_OK);
|
|
failed:
|
|
(void)dodrop_sys_objects();
|
|
dodisconnect();
|
|
return NDBT_ProgramExit(NDBT_FAILED);
|
|
}
|