599 lines
15 KiB
C
599 lines
15 KiB
C
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#define TEST_LARGE
|
|
|
|
#define MAX_COLUMN_NAME_LENGTH 128 //??
|
|
#define MAX_TABLE_NAME_LENGTH 512 //???
|
|
#define MAX_VALUE_LENGTH 2048
|
|
#define MAX_DB_NAME_LENGTH 64
|
|
#define MAX_KEY_PART 15 // 64
|
|
#define MAX_BUF_LENGTH 2048 * 2048
|
|
|
|
#ifdef TEST_MIN
|
|
#define MAX_TABLES 1
|
|
#define MAX_INDEXES 3
|
|
#define MAX_COLUMNS 12
|
|
#define N_ROWS 1
|
|
#endif
|
|
|
|
#ifdef TEST_SMALL
|
|
#define MAX_TABLES 50
|
|
#define MAX_INDEXES 8
|
|
#define MAX_COLUMNS 30
|
|
#define N_ROWS 10
|
|
#endif
|
|
|
|
#ifdef TEST_LARGE
|
|
#define MAX_TABLES 5000
|
|
#define MAX_INDEXES 10
|
|
#define MAX_COLUMNS 30
|
|
#define N_ROWS 100
|
|
#endif
|
|
|
|
#ifdef TEST_BOUNDARY
|
|
#define MAX_TABLES 10
|
|
#define MAX_INDEXES 128
|
|
#define MAX_COLUMNS 512
|
|
#define N_ROWS 10
|
|
#endif
|
|
|
|
enum enum_data_type {
|
|
INT = 0,
|
|
VARCHAR,
|
|
// more ...
|
|
MAX_DATA_TYPE
|
|
};
|
|
typedef enum enum_data_type data_type_t;
|
|
|
|
enum enum_action {
|
|
CREATE_TABLE = -1,
|
|
// DROP_TABLE,
|
|
// RENAME_TABLE,
|
|
ADD_OR_DROP_COLUMN,
|
|
ADD_OR_DROP_INDEX,
|
|
// RENAME_COLUMN,
|
|
// more ...
|
|
MAX_ACTION
|
|
};
|
|
typedef enum enum_action action_t;
|
|
|
|
struct struct_type_value_pair {
|
|
data_type_t type;
|
|
char type_str[64];
|
|
char value[MAX_VALUE_LENGTH];
|
|
};
|
|
typedef struct struct_type_value_pair type_value_pair;
|
|
|
|
const type_value_pair pairs[MAX_DATA_TYPE] = {
|
|
{INT, "int", "5"}, {VARCHAR, "varchar(200)", "\"oceanbase\""}
|
|
// more...
|
|
};
|
|
|
|
struct struct_column_schema {
|
|
bool enabled;
|
|
char cname[MAX_COLUMN_NAME_LENGTH];
|
|
data_type_t data_type;
|
|
bool not_null;
|
|
bool has_default;
|
|
};
|
|
typedef struct struct_column_schema column_schema;
|
|
|
|
struct struct_index_schema {
|
|
bool enabled;
|
|
char index_name[MAX_TABLE_NAME_LENGTH];
|
|
int n_cols;
|
|
int col_indexes[MAX_COLUMNS];
|
|
};
|
|
typedef struct struct_index_schema index_schema;
|
|
|
|
// TODO: index will be usd in index array of column
|
|
struct struct_table_schema {
|
|
bool enabled;
|
|
char table_name[MAX_TABLE_NAME_LENGTH];
|
|
column_schema column_schemas[MAX_COLUMNS];
|
|
index_schema index_schemas[MAX_INDEXES];
|
|
};
|
|
typedef struct struct_table_schema table_schema;
|
|
|
|
struct struct_env {
|
|
char db_name[MAX_DB_NAME_LENGTH];
|
|
table_schema schema[MAX_TABLES];
|
|
int seq;
|
|
int n_rows;
|
|
int n_indexes;
|
|
};
|
|
|
|
/* global variables */
|
|
struct struct_env env;
|
|
bool stop = false;
|
|
|
|
/* common methods */
|
|
void log_msg(char* msg)
|
|
{
|
|
fprintf(stderr, "%s\n", msg);
|
|
}
|
|
|
|
bool sr()
|
|
{
|
|
return rand() % 10 < 5;
|
|
}
|
|
|
|
int init_conn()
|
|
{
|
|
// init tenant and connection
|
|
return 0;
|
|
}
|
|
|
|
int init_schema()
|
|
{
|
|
int t = 0;
|
|
int i = 0;
|
|
int j = 0;
|
|
snprintf(env.db_name, MAX_DB_NAME_LENGTH, "db%d", getppid());
|
|
env.seq = 0;
|
|
for (t = 0; t < MAX_TABLES; t++) {
|
|
table_schema* tbl = &env.schema[t];
|
|
tbl->enabled = true;
|
|
snprintf(tbl->table_name, MAX_TABLE_NAME_LENGTH, "t%d", t);
|
|
for (j = 0; j < MAX_COLUMNS; j++) {
|
|
column_schema* col = &tbl->column_schemas[j];
|
|
col->enabled = sr();
|
|
snprintf(col->cname, MAX_COLUMN_NAME_LENGTH, "c%ld", j);
|
|
col->data_type = j % MAX_DATA_TYPE;
|
|
col->not_null = sr();
|
|
col->has_default = sr();
|
|
}
|
|
|
|
for (i = 0; i < MAX_INDEXES; i++) {
|
|
index_schema* idx = &tbl->index_schemas[i];
|
|
idx->enabled = sr();
|
|
snprintf(idx->index_name, MAX_TABLE_NAME_LENGTH, "idx%d", i);
|
|
int last_idx = 0;
|
|
for (j = 0; j < MAX_COLUMNS; j++) {
|
|
idx->col_indexes[j] = -1;
|
|
if (sr())
|
|
continue;
|
|
if (last_idx >= MAX_KEY_PART)
|
|
continue;
|
|
if (!tbl->column_schemas[j].enabled)
|
|
continue;
|
|
idx->col_indexes[last_idx++] = j;
|
|
}
|
|
idx->n_cols = last_idx;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// TODO: check at least one column in base table
|
|
void dump_schema()
|
|
{
|
|
int t = 0;
|
|
int i = 0;
|
|
int j = 0;
|
|
fprintf(stderr, "\n");
|
|
for (t = 0; t < MAX_TABLES; t++) {
|
|
table_schema* tbl = &env.schema[t];
|
|
assert(tbl->enabled);
|
|
fprintf(stderr, "#base table %s\n", tbl->table_name);
|
|
for (j = 0; j < MAX_COLUMNS; j++) {
|
|
const column_schema* col = &tbl->column_schemas[j];
|
|
if (!col->enabled)
|
|
continue;
|
|
fprintf(stderr, "# %s %s\n", col->cname, pairs[col->data_type].type_str);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
for (i = 0; i < MAX_INDEXES; i++) {
|
|
const index_schema* idx = &tbl->index_schemas[i];
|
|
if (!idx->enabled)
|
|
continue;
|
|
fprintf(stderr, "#index table %s\n", idx->index_name);
|
|
fprintf(stderr, "# ");
|
|
for (j = 0; j < MAX_COLUMNS; j++) {
|
|
if (idx->col_indexes[j] == -1)
|
|
continue;
|
|
const column_schema* col = &tbl->column_schemas[idx->col_indexes[j]];
|
|
assert(col->enabled);
|
|
fprintf(stderr, "%s ", col->cname);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
void init_table(int tid)
|
|
{
|
|
const table_schema* tbl = &env.schema[tid];
|
|
char table_define[MAX_BUF_LENGTH];
|
|
memset(table_define, 0, MAX_BUF_LENGTH);
|
|
snprintf(table_define, MAX_BUF_LENGTH, "DROP TABLE IF EXISTS %s;", tbl->table_name);
|
|
log_msg(table_define);
|
|
int pos = snprintf(table_define, MAX_BUF_LENGTH, "CREATE TABLE %s(", tbl->table_name);
|
|
bool need_comma = false;
|
|
int t = 0;
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
for (j = 0; j < MAX_COLUMNS; j++) {
|
|
const column_schema* col = &tbl->column_schemas[j];
|
|
if (!col->enabled)
|
|
continue;
|
|
char defaults[MAX_BUF_LENGTH];
|
|
snprintf(defaults, MAX_BUF_LENGTH, "DEFAULT '%s'", pairs[col->data_type].value);
|
|
if (need_comma)
|
|
pos += snprintf(table_define + pos, MAX_BUF_LENGTH, ", ");
|
|
pos += snprintf(table_define + pos,
|
|
MAX_BUF_LENGTH,
|
|
"%s %s %s %s",
|
|
col->cname,
|
|
pairs[col->data_type].type_str,
|
|
col->not_null ? "NOT NULL" : "",
|
|
col->has_default ? defaults : "");
|
|
need_comma = true;
|
|
}
|
|
|
|
for (i = 0; i < MAX_INDEXES; i++) {
|
|
const index_schema* idx = &tbl->index_schemas[i];
|
|
if (!idx->enabled)
|
|
continue;
|
|
if (idx->n_cols == 0)
|
|
continue;
|
|
char index_define[MAX_BUF_LENGTH];
|
|
int index_pos = 0;
|
|
memset(index_define, 0, MAX_BUF_LENGTH);
|
|
bool index_need_comma = false;
|
|
index_pos += snprintf(index_define, MAX_BUF_LENGTH, " INDEX %s(", idx->index_name);
|
|
for (j = 0; j < idx->n_cols; j++) {
|
|
const column_schema* col = &tbl->column_schemas[idx->col_indexes[j]];
|
|
assert(col->enabled);
|
|
if (j != 0)
|
|
index_pos += snprintf(index_define + index_pos, MAX_BUF_LENGTH, ",");
|
|
index_pos += snprintf(index_define + index_pos, MAX_BUF_LENGTH, "%s", col->cname);
|
|
}
|
|
snprintf(index_define + index_pos, MAX_BUF_LENGTH, ")");
|
|
if (need_comma)
|
|
pos += snprintf(table_define + pos, MAX_BUF_LENGTH, ",");
|
|
pos += snprintf(table_define + pos, MAX_BUF_LENGTH, "%s", index_define);
|
|
need_comma = true;
|
|
}
|
|
|
|
snprintf(table_define + pos, MAX_BUF_LENGTH, ");");
|
|
log_msg(table_define);
|
|
}
|
|
|
|
void init_db()
|
|
{
|
|
char db_define[MAX_BUF_LENGTH];
|
|
snprintf(db_define,
|
|
MAX_BUF_LENGTH,
|
|
"DROP DATABASE IF EXISTS %s; CREATE DATABASE %s; use %s;\n",
|
|
env.db_name,
|
|
env.db_name,
|
|
env.db_name);
|
|
log_msg(db_define);
|
|
}
|
|
|
|
void init_ob()
|
|
{
|
|
int t = 0;
|
|
init_db();
|
|
for (t = 0; t < MAX_TABLES; t++)
|
|
init_table(t);
|
|
}
|
|
|
|
int init()
|
|
{
|
|
init_schema();
|
|
init_conn();
|
|
dump_schema();
|
|
init_ob();
|
|
return 0;
|
|
}
|
|
|
|
action_t gen_action()
|
|
{
|
|
action_t action = random() % MAX_ACTION;
|
|
return action;
|
|
}
|
|
|
|
bool any_index_has_col(int tid, int cid)
|
|
{
|
|
int i, j;
|
|
const table_schema* tbl = &env.schema[tid];
|
|
for (i = 0; i < MAX_INDEXES; i++) {
|
|
const index_schema* idx = &tbl->index_schemas[i];
|
|
for (j = 0; j < MAX_COLUMNS; j++) {
|
|
if (idx->col_indexes[j] == cid)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void add_or_drop_column(int tid)
|
|
{
|
|
char alter_define[MAX_BUF_LENGTH];
|
|
memset(alter_define, 0, MAX_BUF_LENGTH);
|
|
table_schema* tbl = &env.schema[tid];
|
|
bool ok = false;
|
|
int pos =
|
|
snprintf(alter_define, MAX_BUF_LENGTH, "ALTER TABLE %s /* add_or_drop_column %d */", tbl->table_name, env.seq++);
|
|
bool need_comma = false;
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
for (i = 0; i < MAX_COLUMNS; i++) {
|
|
if (sr())
|
|
continue;
|
|
column_schema* col = &tbl->column_schemas[i];
|
|
if (col->enabled) {
|
|
// drop column
|
|
// ob-1.0 don't allow drop column that belongs to some index, therefore, will not act the same with mysql that,
|
|
// index will be automatedly dropped if owned columns are all dropped.
|
|
if (any_index_has_col(tid, i))
|
|
continue;
|
|
if (need_comma)
|
|
pos += snprintf(alter_define + pos, MAX_BUF_LENGTH, ",");
|
|
pos += snprintf(alter_define + pos, MAX_BUF_LENGTH, " DROP COLUMN %s ", col->cname);
|
|
if (!ok)
|
|
ok = true;
|
|
need_comma = true;
|
|
col->enabled = false;
|
|
} else if (!col->enabled) {
|
|
char defaults[MAX_BUF_LENGTH];
|
|
snprintf(defaults, MAX_BUF_LENGTH, "DEFAULT '%s'", pairs[col->data_type].value);
|
|
if (need_comma)
|
|
pos += snprintf(alter_define + pos, MAX_BUF_LENGTH, ",");
|
|
pos += snprintf(alter_define + pos,
|
|
MAX_BUF_LENGTH,
|
|
" ADD COLUMN %s %s %s %s",
|
|
col->cname,
|
|
pairs[col->data_type].type_str,
|
|
col->not_null ? "NOT NULL" : "",
|
|
col->has_default ? defaults : "");
|
|
if (!ok)
|
|
ok = true;
|
|
need_comma = true;
|
|
col->enabled = true;
|
|
}
|
|
}
|
|
snprintf(alter_define + pos, MAX_BUF_LENGTH, ";");
|
|
|
|
// update index if column is dropped
|
|
for (i = 0; i < MAX_INDEXES; i++) {
|
|
index_schema* idx = &tbl->index_schemas[i];
|
|
int n_drops = 0;
|
|
for (j = 0; j < idx->n_cols; j++) {
|
|
if (!tbl->column_schemas[idx->col_indexes[j]].enabled) {
|
|
idx->col_indexes[j] = -1;
|
|
n_drops++;
|
|
}
|
|
}
|
|
|
|
int t_drops = n_drops;
|
|
while (t_drops--) {
|
|
for (j = 0; j < idx->n_cols; j++) {
|
|
if (idx->col_indexes[j] == -1) {
|
|
idx->col_indexes[j] = idx->col_indexes[j + 1];
|
|
idx->col_indexes[j + 1] = -1;
|
|
}
|
|
}
|
|
}
|
|
idx->n_cols -= n_drops;
|
|
}
|
|
|
|
#if 0
|
|
if (!ok) {
|
|
// drop all indexes
|
|
for (i=1; i<MAX_INDEXES; i++) {
|
|
table_schema *tbl = &env.schema[i];
|
|
tbl->enabled = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
log_msg(alter_define);
|
|
}
|
|
|
|
void shuffer_index(int tid)
|
|
{
|
|
int i = 1;
|
|
int j = 0;
|
|
table_schema* tbl = &env.schema[tid];
|
|
|
|
for (i = 0; i < MAX_INDEXES; i++) {
|
|
if (sr())
|
|
continue;
|
|
index_schema* idx = &tbl->index_schemas[i];
|
|
if (idx->enabled)
|
|
continue;
|
|
int last_idx = 0;
|
|
for (j = 0; j < MAX_COLUMNS; j++) {
|
|
idx->col_indexes[j] = -1;
|
|
if (sr())
|
|
continue;
|
|
if (last_idx >= MAX_KEY_PART)
|
|
continue;
|
|
if (!tbl->column_schemas[j].enabled)
|
|
continue;
|
|
idx->col_indexes[last_idx++] = j;
|
|
}
|
|
idx->n_cols = last_idx;
|
|
}
|
|
}
|
|
|
|
void add_or_drop_index(int tid)
|
|
{
|
|
char alter_define[MAX_BUF_LENGTH];
|
|
memset(alter_define, 0, MAX_BUF_LENGTH);
|
|
bool ok = false;
|
|
table_schema* tbl = &env.schema[tid];
|
|
int pos =
|
|
snprintf(alter_define, MAX_BUF_LENGTH, "ALTER TABLE %s /* add_or_drop_index %d */", tbl->table_name, env.seq++);
|
|
bool need_comma = false;
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
for (i = 0; i < MAX_INDEXES; i++) {
|
|
if (sr())
|
|
continue;
|
|
|
|
index_schema* idx = &tbl->index_schemas[i];
|
|
if (idx->enabled) {
|
|
// drop index
|
|
if (need_comma)
|
|
pos += snprintf(alter_define + pos, MAX_BUF_LENGTH, ",");
|
|
pos += snprintf(alter_define + pos, MAX_BUF_LENGTH, " DROP INDEX %s", idx->index_name);
|
|
need_comma = true;
|
|
idx->enabled = false;
|
|
} else {
|
|
// add index
|
|
if (idx->n_cols == 0)
|
|
continue;
|
|
|
|
char add_index_str[MAX_BUF_LENGTH];
|
|
memset(add_index_str, 0, MAX_BUF_LENGTH);
|
|
int add_index_pos = 0;
|
|
bool index_need_comma = false;
|
|
add_index_pos += snprintf(add_index_str + add_index_pos, MAX_BUF_LENGTH, " ADD INDEX %s(", idx->index_name);
|
|
for (j = 0; j < idx->n_cols; j++) {
|
|
const column_schema* col = &tbl->column_schemas[idx->col_indexes[j]];
|
|
assert(col->enabled);
|
|
if (j != 0)
|
|
add_index_pos += snprintf(add_index_str + add_index_pos, MAX_BUF_LENGTH, ",");
|
|
add_index_pos += snprintf(add_index_str + add_index_pos, MAX_BUF_LENGTH, " %s", col->cname);
|
|
}
|
|
idx->enabled = true;
|
|
|
|
snprintf(add_index_str + add_index_pos, MAX_BUF_LENGTH, ")");
|
|
if (need_comma)
|
|
pos += snprintf(alter_define + pos, MAX_BUF_LENGTH, ",");
|
|
pos += snprintf(alter_define + pos, MAX_BUF_LENGTH, "%s", add_index_str);
|
|
need_comma = true;
|
|
}
|
|
}
|
|
|
|
snprintf(alter_define + pos, MAX_BUF_LENGTH, ";");
|
|
|
|
#if 0
|
|
if (!need_comma) {
|
|
// alter table, drop all indexes
|
|
for (i=1; i<MAX_INDEXES; i++) {
|
|
table_schema *tbl = &env.schema[i];
|
|
tbl->enabled = false;
|
|
}
|
|
}
|
|
#endif
|
|
log_msg(alter_define);
|
|
}
|
|
|
|
void fill_table(int tid, int n)
|
|
{
|
|
int i = 0;
|
|
table_schema* tbl = &env.schema[tid];
|
|
char insert_define[MAX_BUF_LENGTH];
|
|
memset(insert_define, 0, MAX_BUF_LENGTH);
|
|
|
|
bool need_comma = false;
|
|
int pos = snprintf(insert_define, MAX_BUF_LENGTH, "INSERT INTO %s(", tbl->table_name);
|
|
for (i = 0; i < MAX_COLUMNS; i++) {
|
|
const column_schema* col = &tbl->column_schemas[i];
|
|
if (!col->enabled)
|
|
continue;
|
|
if (need_comma)
|
|
pos += snprintf(insert_define + pos, MAX_BUF_LENGTH, ", ");
|
|
pos += snprintf(insert_define + pos, MAX_BUF_LENGTH, "%s", col->cname);
|
|
need_comma = true;
|
|
}
|
|
pos += snprintf(insert_define + pos, MAX_BUF_LENGTH, ") VALUES(", tbl->table_name);
|
|
|
|
while (n-- > 0) {
|
|
bool need_comma = false;
|
|
for (i = 0; i < MAX_COLUMNS; i++) {
|
|
const column_schema* col = &tbl->column_schemas[i];
|
|
if (col->enabled) {
|
|
if (need_comma)
|
|
pos += snprintf(insert_define + pos, MAX_BUF_LENGTH, ",");
|
|
pos += snprintf(insert_define + pos, MAX_BUF_LENGTH, "%s", pairs[col->data_type].value);
|
|
need_comma = true;
|
|
}
|
|
}
|
|
if (n > 0)
|
|
pos += snprintf(insert_define + pos, MAX_BUF_LENGTH, "), (");
|
|
}
|
|
pos += snprintf(insert_define + pos, MAX_BUF_LENGTH, ");");
|
|
log_msg(insert_define);
|
|
}
|
|
|
|
void fill_data(int n)
|
|
{
|
|
int t = 0;
|
|
for (t = 0; t < MAX_TABLES; t++) {
|
|
fill_table(t, n);
|
|
}
|
|
}
|
|
|
|
int do_action(action_t action)
|
|
{
|
|
int tid = rand() % MAX_TABLES;
|
|
switch (action) {
|
|
case ADD_OR_DROP_COLUMN:
|
|
add_or_drop_column(tid);
|
|
break;
|
|
case ADD_OR_DROP_INDEX:
|
|
shuffer_index(tid);
|
|
add_or_drop_index(tid);
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int do_work()
|
|
{
|
|
action_t action;
|
|
action = gen_action();
|
|
do_action(action);
|
|
}
|
|
|
|
int start(int n)
|
|
{
|
|
int loops = 0;
|
|
while (!stop) {
|
|
do_work();
|
|
dump_schema();
|
|
fill_data(N_ROWS);
|
|
if (++loops % n == 0) {
|
|
log_msg("#finished");
|
|
stop = true;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, void* argv[])
|
|
{
|
|
init();
|
|
start(atoi(argv[1]));
|
|
}
|