/** * 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 #include #include #include #include #include #include #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; tenabled = true; snprintf(tbl->table_name, MAX_TABLE_NAME_LENGTH, "t%d", t); for (j=0; jcolumn_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; iindex_schemas[i]; idx->enabled = sr(); snprintf(idx->index_name, MAX_TABLE_NAME_LENGTH, "idx%d", i); int last_idx = 0; for (j=0; jcol_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; tenabled); fprintf(stderr, "#base table %s\n", tbl->table_name); for (j=0; jcolumn_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; iindex_schemas[i]; if (!idx->enabled) continue; fprintf(stderr, "#index table %s\n", idx->index_name); fprintf(stderr, "# "); for (j=0; jcol_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; jcolumn_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; iindex_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; jn_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; tindex_schemas[i]; for (j=0; jcol_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; icolumn_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; iindex_schemas[i]; int n_drops = 0; for (j=0; jn_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; jn_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; ienabled = 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; iindex_schemas[i]; if (idx->enabled) continue; int last_idx = 0; for (j=0; jcol_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; iindex_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; jn_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; ienabled = 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; icolumn_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; icolumn_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