[FEAT MERGE] oblogminer[patch from 4.2.3]

Co-authored-by: qiuyg3 <qiuyg3@chinaunicom.cn>
This commit is contained in:
nautaa
2024-05-31 04:59:38 +00:00
committed by ant-ob-hengtang
parent 0da007833b
commit ed28f133d6
91 changed files with 14937 additions and 2 deletions

View File

@ -32,3 +32,4 @@ add_subdirectory(share)
add_subdirectory(rootserver)
add_subdirectory(tools)
add_subdirectory(data_dictionary)
add_subdirectory(logminer)

View File

@ -0,0 +1,27 @@
set(UTIL_FILE_NAME ob_log_miner_test_utils.cpp)
function(logminer_unittest case)
if(ARGC EQUAL 1)
add_executable(${case} ${case}.cpp ${UTIL_FILE_NAME})
else()
add_executable(${ARGV} ${UTIL_FILE_NAME})
endif()
if (case MATCHES "^test_.*")
add_test(${case} ${case})
set_tests_properties(${case} PROPERTIES TIMEOUT 300)
endif()
target_link_libraries(${case} PRIVATE oblogminer_obj_dev gtest gmock)
target_include_directories(${case}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/unittest ${CMAKE_SOURCE_DIR}/deps/oblib/unittest)
endfunction()
logminer_unittest(test_ob_log_miner_args)
logminer_unittest(test_ob_log_miner_br_filter)
logminer_unittest(test_ob_log_miner_record)
logminer_unittest(test_ob_log_miner_record_converter)
logminer_unittest(test_ob_log_miner_utils)
logminer_unittest(test_ob_log_miner_analyzer_checkpoint)
logminer_unittest(test_ob_log_miner_progress_range)
logminer_unittest(test_ob_log_miner_file_meta)
logminer_unittest(test_ob_log_miner_file_index)
logminer_unittest(test_ob_log_miner_file_manager)

View File

@ -0,0 +1,186 @@
/**
* Copyright (c) 2023 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 "ob_log_binlog_record.h"
#include "ob_log_miner_br.h"
#include "ob_log_miner_test_utils.h"
#include "gtest/gtest.h"
namespace oceanbase
{
namespace oblogminer
{
ObLogMinerBR *v_build_logminer_br(binlogBuf *new_bufs,
binlogBuf *old_bufs,
RecordType type,
lib::Worker::CompatMode compat_mode,
const char *db_name,
const char *table_name,
const char *encoding,
const int arg_count, va_list *ap)
{
ObLogMinerBR *br = new ObLogMinerBR;
EXPECT_NE(nullptr, br);
libobcdc::ObLogBR *cdc_br = new libobcdc::ObLogBR;
cdc_br->init_data(EINSERT, 0, 1, 0, ObString(), ObString(), ObString(),
100, 100);
ICDCRecord *rec = DRCMessageFactory::createBinlogRecord();
rec->setUserData(cdc_br);
EXPECT_NE(nullptr, rec);
ITableMeta *tab_meta = DRCMessageFactory::createTableMeta();
EXPECT_NE(nullptr, tab_meta);
IDBMeta *db_meta = DRCMessageFactory::createDBMeta();
EXPECT_NE(nullptr, db_meta);
rec->setNewColumn(new_bufs, arg_count/3);
rec->setOldColumn(old_bufs, arg_count/3);
rec->setRecordType(type);
rec->setDbname(db_name);
rec->setTbname(table_name);
db_meta->setName(db_name);
tab_meta->setPKs("");
tab_meta->setPkinfo("");
tab_meta->setUKs("");
tab_meta->setUkinfo("");
tab_meta->setName(table_name);
tab_meta->setDBMeta(db_meta);
IColMeta *col_meta = nullptr;
const char *next = nullptr;
int64_t arg_idx = 0;
for (int i = 0; i < arg_count; i++) {
const char *col_arg = nullptr;
int len = 0;
int data_type = 0;
if (3 > i%4) {
col_arg = va_arg(*ap, const char *);
len = col_arg == nullptr ? 0 : strlen(col_arg);
} else {
data_type = va_arg(*ap, int);
}
if (0 == i%4) { // column name
col_meta = DRCMessageFactory::createColMeta();
EXPECT_NE(col_meta, nullptr);
col_meta->setName(col_arg);
col_meta->setEncoding(encoding);
tab_meta->append(col_arg, col_meta);
} else if (1 == i%4 && EDELETE != type) { // new value, EDELETE hasn't new value
rec->putNew(col_arg, len);
} else if (2 == i%4 && EINSERT != type) { // old value, EINSERT hasn't old value
rec->putOld(col_arg, len);
} else if (3 == i%4) { // column data type
col_meta->setType(data_type);
}
}
rec->setTableMeta(tab_meta);
br->init(nullptr, rec, compat_mode, 0, 1, 0);
return br;
}
ObLogMinerBR *build_logminer_br(binlogBuf *new_bufs,
binlogBuf *old_bufs,
RecordType type,
lib::Worker::CompatMode compat_mode,
const char *db_name,
const char *table_name,
const char *encoding,
const int arg_count, ...)
{
va_list ap;
ObLogMinerBR *br = nullptr;
va_start(ap, arg_count);
br = v_build_logminer_br(new_bufs, old_bufs, type,
compat_mode, db_name, table_name, encoding, arg_count, &ap);
va_end(ap);
return br;
}
ObLogMinerBR *build_logminer_br(binlogBuf *new_bufs,
binlogBuf *old_bufs,
RecordType type,
lib::Worker::CompatMode compat_mode,
const char *db_name,
const char *table_name,
const int arg_count, ...)
{
va_list ap;
ObLogMinerBR *br = nullptr;
va_start(ap, arg_count);
br = v_build_logminer_br(new_bufs, old_bufs, type,
compat_mode, db_name, table_name, "utf8mb4", arg_count, &ap);
va_end(ap);
return br;
}
void destroy_miner_br(ObLogMinerBR *&br) {
ICDCRecord *cdc_br = br->get_br();
IDBMeta *db_meta = cdc_br->getDBMeta();
ITableMeta *tbl_meta = cdc_br->getTableMeta();
delete static_cast<libobcdc::ObLogBR*>(cdc_br->getUserData());
DRCMessageFactory::destroy(db_meta);
DRCMessageFactory::destroy(tbl_meta);
DRCMessageFactory::destroy(cdc_br);
delete br;
br = nullptr;
}
ObLogMinerRecord *build_logminer_record(ObIAllocator &alloc,
lib::Worker::CompatMode compat_mode,
uint64_t tenant_id,
int64_t orig_cluster_id,
const char *tenant_name,
const char *database_name,
const char *tbl_name,
int64_t trans_id,
const char* const * pks,
const int64_t pk_cnt,
const char* const * uks,
const int64_t uk_cnt,
const char* row_unique_id,
RecordType record_type,
int64_t commit_ts_us,
const char * redo_stmt,
const char * undo_stmt)
{
void *ptr = ob_malloc(sizeof(ObLogMinerRecord), "testminer_rec");
ObLogMinerRecord *rec = new (ptr) (ObLogMinerRecord);
rec->set_allocator(&alloc);
rec->compat_mode_ = compat_mode;
rec->tenant_id_ = tenant_id;
rec->orig_cluster_id_ = orig_cluster_id;
rec->tenant_name_.assign(tenant_name);
rec->database_name_.assign(database_name);
rec->table_name_.assign(tbl_name);
rec->trans_id_ = trans_id;
for (int i = 0; i < pk_cnt; i++) {
rec->primary_keys_.push_back(pks[i]);
}
for (int i = 0; i < uk_cnt; i++) {
rec->unique_keys_.push_back(uks[i]);
}
rec->row_unique_id_ = row_unique_id;
rec->record_type_ = record_type;
rec->commit_scn_.convert_from_ts(commit_ts_us);
rec->redo_stmt_.append(redo_stmt);
rec->undo_stmt_.append(undo_stmt);
return rec;
}
void destroy_miner_record(ObLogMinerRecord *&rec)
{
rec->redo_stmt_.reset();
rec->undo_stmt_.reset();
ob_free(rec);
}
}
}

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) 2023 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.
*/
#ifndef OCEANBASE_LOG_MINER_UNITTEST_UTILS_H_
#define OCEANBASE_LOG_MINER_UNITTEST_UTILS_H_
#include "ob_log_miner_br.h"
#include "rpc/obmysql/ob_mysql_global.h"
#include "gtest/gtest.h"
#define private public
#include "ob_log_miner_record.h"
#undef private
namespace oceanbase
{
namespace oblogminer
{
ObLogMinerBR *build_logminer_br(binlogBuf *new_bufs,
binlogBuf *old_bufs,
RecordType type,
lib::Worker::CompatMode compat_mode,
const char *db_name,
const char *table_name,
const int arg_count, ...);
ObLogMinerBR *build_logminer_br(binlogBuf *new_bufs,
binlogBuf *old_bufs,
RecordType type,
lib::Worker::CompatMode compat_mode,
const char *db_name,
const char *table_name,
const char *encoding,
const int arg_count, ...);
void destroy_miner_br(ObLogMinerBR *&br);
ObLogMinerRecord *build_logminer_record(ObIAllocator &alloc,
lib::Worker::CompatMode compat_mode,
uint64_t tenant_id,
int64_t orig_cluster_id,
const char *tenant_name,
const char *database_name,
const char *tbl_name,
int64_t trans_id,
const char* const * pks,
const int64_t pk_cnt,
const char* const * uks,
const int64_t uk_cnt,
const char* row_unique_id,
RecordType record_type,
int64_t commit_ts_us,
const char * redo_stmt,
const char * undo_stmt);
void destroy_miner_record(ObLogMinerRecord *&rec);
}
}
#endif

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2023 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 "ob_log_miner_analyzer_checkpoint.h"
#include "lib/oblog/ob_log.h"
#include "gtest/gtest.h"
namespace oceanbase
{
namespace oblogminer
{
TEST(test_log_miner_analyzer_ckpt, SerializeFunc)
{
ObLogMinerCheckpoint ckpt, ckpt1;
int64_t pos = 0;
const char *buf1 = "PROGRESS=1\nCUR_FILE_ID=3\nMAX_FILE_ID=2\n";
char tmp_buf[1000];
EXPECT_EQ(OB_SUCCESS, ckpt.deserialize(buf1, strlen(buf1), pos));
EXPECT_EQ(ckpt.progress_, 1);
EXPECT_EQ(ckpt.cur_file_id_, 3);
EXPECT_EQ(ckpt.max_file_id_, 2);
pos = 0;
EXPECT_EQ(OB_SUCCESS, ckpt.serialize(tmp_buf, 1000, pos));
tmp_buf[pos+1] = '\0';
EXPECT_STREQ(buf1, tmp_buf);
pos = 0;
const char *buf2 = "PROGRESS=a\nCUR_FILE_ID=3\nMAX_FILE_ID=2\n";
EXPECT_EQ(OB_INVALID_DATA, ckpt.deserialize(buf2, strlen(buf2), pos));
pos = 0;
const char *buf3 = "PROGRESS=1\nCUR_FILE_ID=3MAX_FILE_ID=2\n";
EXPECT_EQ(OB_INVALID_DATA, ckpt.deserialize(buf3, strlen(buf3), pos));
pos = 0;
for (int i = 0; i < 10000; i++) {
int64_t pos1 = 0;
pos = 0;
ckpt.cur_file_id_ = rand();
ckpt.max_file_id_ = rand();
ckpt.progress_ = rand();
EXPECT_EQ(OB_SUCCESS, ckpt.serialize(tmp_buf, 1000, pos));
EXPECT_EQ(pos, ckpt.get_serialize_size());
EXPECT_EQ(OB_SUCCESS, ckpt1.deserialize(tmp_buf, pos, pos1));
EXPECT_EQ(ckpt, ckpt1);
}
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_analyzer_checkpoint.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_analyzer_checkpoint.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,520 @@
/**
* Copyright (c) 2023 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 "gtest/gtest.h"
#include "lib/oblog/ob_log.h"
#include "ob_log_miner_br_filter.h"
#include "ob_log_miner_filter_condition.h"
#include "ob_log_miner_br.h"
#include "ob_log_miner_test_utils.h"
using namespace oceanbase;
namespace oceanbase
{
namespace oblogminer
{
TEST(ob_log_miner_br_filter, ColumnBRFilterPlugin)
{
ObArenaAllocator arena_alloc("FilterTest");
ColumnBRFilterPlugin col_filter(&arena_alloc);
ObLogMinerBR *br = nullptr;
bool need_filter = false;
const int buf_cnt = 10;
binlogBuf *new_buf = static_cast<binlogBuf*>(arena_alloc.alloc(sizeof(binlogBuf) * buf_cnt));
binlogBuf *old_buf = static_cast<binlogBuf*>(arena_alloc.alloc(sizeof(binlogBuf) * buf_cnt));
for (int i = 0; i < 10; i++) {
new_buf[i].buf = static_cast<char*>(arena_alloc.alloc(1024));
new_buf[i].buf_size = 1024;
new_buf[i].buf_used_size = 0;
old_buf[i].buf = static_cast<char*>(arena_alloc.alloc(1024));
old_buf[i].buf_size = 1024;
old_buf[i].buf_used_size = 0;
}
EXPECT_EQ(OB_SUCCESS, col_filter.init("[]"));
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", "val1", "val2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[{}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[{\"table_name\":\"aaa\", \
\"table_name\":\"aaa\"}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[{\"tenant_name\":\"aaa\", \
\"table_name\":\"aaa\"}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[{\"database_name\":\"aaa\", \
\"database_name\":\"aaa\"}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[{\"database_name\":\"t1.aaa\", \
\"table_name\":\"aaa\"}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[{\"database_name\":\"t1.aaa\", \
\"table_name\":\"aaa\", \"column_cond\":[{\"col1\": 3.1415926}]}]"));
col_filter.destroy();
EXPECT_EQ(OB_SUCCESS, col_filter.init("[{\"database_name\":\"t1.aaa\", \
\"table_name\":\"aaa\", \"column_cond\":[{\"col1\": \"3.1415926\",\"col2\":\"abcde\"}]}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[{\"database_name\":\"t1.aaa\", \
\"table_name\":\"aaa\", \"column_cond\":[{\"col1\": \"3.1415926\",\"col2\":\"abcde\"}]"));
col_filter.destroy();
EXPECT_EQ(OB_SUCCESS, col_filter.init("[{\"database_name\":\"t1.aaa\", \
\"table_name\":\"aaa\", \"column_cond\":[{\"col1\": \"3.1415926\",\"col2\":\"abcde\"}, {\"col3\":null}]}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[\"table_name\":\"aaa\", "
"\"column_cond\":[{\"col1\": \"3.1415926\",\"col2\":\"abcde\"},{\"col3\":null}]}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("[\"column_cond\":[{\"col1\": \"3.1415926\",\"col2\":\"abcde\"},{\"col3\":null}]}]"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("db1.t1|db2.t2|db3.t*"));
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("db1.*|db2"));
col_filter.destroy();
EXPECT_EQ(OB_SUCCESS, col_filter.init("["
"{\"database_name\":\"db1\",\"table_name\":\"table1\","
"\"column_cond\":[{\"col1\":\"val1\"},{\"col2\":\"val2\"}]"
"}"
"]"));
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", "val1", "val2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col2", "val3", "val2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col2", "val3", "val3",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING), "col1", nullptr, "val1",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db2", "table1", 8, "col2", "val3", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col1", "val1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table2", 8, "col2", "val3", "val3",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col1", nullptr, "val1", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col2", "val3", "val3",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col3", nullptr, "val4", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EDDL, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table2", 0);
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
col_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, col_filter.init("db*.t1|*_prod.*_history|db3.t*"));
col_filter.destroy();
EXPECT_EQ(OB_SUCCESS, col_filter.init(
"[{\"database_name\":\"db1\", \"table_name\":\"table1\","
"\"column_cond\":[{\"col1\":\"val1\", \"col2\":\"val2\"}]}]"));
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db2", "non_spec_tbl", 3, "non_spec_col", "non_spec_val", nullptr);
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db0", "non_spec_tbl", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db3", "non_spec_tbl", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db3", "tmp_tbl", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", "val2", "val2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val1", "val3", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", "val1", "val1",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val1", "val3", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", "val10", "val100",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val2", "val2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", "val1", "val2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val1", "val2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", nullptr, "val1",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", nullptr, "val2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 12, "col1", "val10", "val1",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val20", "val2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col3", "val3", "val30", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
col_filter.destroy();
// null value filter
EXPECT_EQ(OB_SUCCESS, col_filter.init(
"[{\"database_name\":\"db1\", \"table_name\":\"table1\","
"\"column_cond\":[{\"col1\":null}]}]"));
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db2", "non_spec_tbl", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE,lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", "non_spec_val", "xx",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", nullptr, nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", nullptr, nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", nullptr, "xx",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
col_filter.destroy();
// multi column filter with null
EXPECT_EQ(OB_SUCCESS, col_filter.init(
"[{\"database_name\":\"db1\", \"table_name\":\"table1\","
"\"column_cond\":[{\"col1\":\"val1\", \"col2\":\"val2\"}, {\"col3\":null}]}]"));
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db2", "non_spec_tbl", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT,lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col3", nullptr, nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", "val1", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val2", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", "val1", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
col_filter.destroy();
// multi column filter
EXPECT_EQ(OB_SUCCESS, col_filter.init(
"[{\"database_name\":\"db1\", \"table_name\":\"table1\","
"\"column_cond\":[{\"col1\":\"val1\", \"col2\":\"val2\"}, {\"col3\":\"val3\", \"col4\":\"val4\"}]}]"));
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db2", "non_spec_tbl", 4, "non_spec_col", "non_spec_val", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", "val1", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", "val1", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val2", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 8, "col1", "val1", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col3", "val3", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, col_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
col_filter.destroy();
}
TEST(ob_log_miner_br_filter, OperationBRFilterPlugin)
{
OperationBRFilterPlugin op_filter;
ObArenaAllocator arena_alloc("OpFilterTest");
const int64_t buf_cnt = 10;
ObLogMinerBR *br = nullptr;
bool need_filter = false;
binlogBuf *new_buf = static_cast<binlogBuf*>(arena_alloc.alloc(sizeof(binlogBuf) * buf_cnt));
binlogBuf *old_buf = static_cast<binlogBuf*>(arena_alloc.alloc(sizeof(binlogBuf) * buf_cnt));
for (int i = 0; i < 10; i++) {
new_buf[i].buf = static_cast<char*>(arena_alloc.alloc(1024));
new_buf[i].buf_size = 1024;
new_buf[i].buf_used_size = 0;
old_buf[i].buf = static_cast<char*>(arena_alloc.alloc(1024));
old_buf[i].buf_size = 1024;
old_buf[i].buf_used_size = 0;
}
EXPECT_EQ(OB_INVALID_ARGUMENT, op_filter.init("aaa|bbb"));
op_filter.destroy();
EXPECT_EQ(OB_SUCCESS, op_filter.init("Insert"));
op_filter.destroy();
EXPECT_EQ(OB_INVALID_ARGUMENT, op_filter.init("lnsert"));
op_filter.destroy();
EXPECT_EQ(OB_ERR_UNEXPECTED, op_filter.init(nullptr));
op_filter.destroy();
EXPECT_EQ(OB_SUCCESS, op_filter.init("insert"));
br = build_logminer_br(new_buf, old_buf, EBEGIN, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, HEARTBEAT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
op_filter.destroy();
EXPECT_EQ(OB_SUCCESS, op_filter.init("insert|update"));
br = build_logminer_br(new_buf, old_buf, HEARTBEAT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
op_filter.destroy();
EXPECT_EQ(OB_SUCCESS, op_filter.init("insert|update|delete"));
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", "val10", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 12, "col1", "val10", "val1",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col2", "val20", "val2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"col3", "val3", "val30", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 4, "col1", nullptr, "val1",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EBEGIN, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, ECOMMIT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, HEARTBEAT, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EBEGIN, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(false, need_filter);
destroy_miner_br(br);
br = build_logminer_br(new_buf, old_buf, EREPLACE, lib::Worker::CompatMode::MYSQL,
"tenant1.db1", "table1", 0);
EXPECT_EQ(OB_SUCCESS, op_filter.filter(*br, need_filter));
EXPECT_EQ(true, need_filter);
destroy_miner_br(br);
op_filter.destroy();
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_filter.log");
ObLogger &logger = ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_filter.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,160 @@
/**
* Copyright (c) 2023 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.
*/
#define private public
#include "ob_log_miner_file_index.h"
#undef private
#include "gtest/gtest.h"
namespace oceanbase
{
namespace oblogminer
{
static bool operator==(const FileIndex &lhs, const FileIndex &rhs)
{
bool bret = true;
if (lhs.index_file_len_ != rhs.index_file_len_) {
bret = false;
} else if (lhs.index_array_.count() != rhs.index_array_.count()) {
bret = false;
} else {
for (int64_t i = 0; bret && i < lhs.index_array_.count(); i++) {
const FileIndexItem *lhs_item = lhs.index_array_.at(i);
const FileIndexItem *rhs_item = rhs.index_array_.at(i);
if (*lhs_item == *rhs_item) {
} else {
bret = false;
}
}
}
return bret;
}
TEST(test_ob_log_miner_file_index, FileIndexItem)
{
FileIndexItem item, item1;
const int64_t buf_len = 300;
char buf[buf_len];
int64_t pos = 0;
const char *buf1 = "1:2:3\n";
EXPECT_EQ(OB_SUCCESS, item.deserialize(buf1, strlen(buf1), pos));
EXPECT_EQ(item.file_id_, 1);
EXPECT_EQ(item.range_.min_commit_ts_, 2);
EXPECT_EQ(item.range_.max_commit_ts_, 3);
pos = 0;
const char *buf2 = "1:2:3";
EXPECT_EQ(OB_SIZE_OVERFLOW, item.deserialize(buf2, strlen(buf2), pos));
pos = 0;
const char *buf3 = "1:a:3\n";
EXPECT_EQ(OB_INVALID_DATA, item.deserialize(buf3, strlen(buf3), pos));
pos = 0;
const char *buf4 = "1|2|3\n";
EXPECT_EQ(OB_INVALID_DATA, item.deserialize(buf4, strlen(buf4), pos));
pos = 0;
for (int i = 0; i < 10000; i++) {
item1.file_id_ = rand();
item1.range_.min_commit_ts_ = rand();
item1.range_.max_commit_ts_ = rand();
EXPECT_EQ(OB_SUCCESS, item1.serialize(buf, buf_len, pos));
EXPECT_EQ(item1.get_serialize_size(), pos);
pos = 0;
EXPECT_EQ(OB_SUCCESS, item.deserialize(buf, buf_len, pos));
EXPECT_EQ(item, item1);
pos = 0;
}
}
TEST(test_ob_log_miner_file_index, FileIndex)
{
FileIndex index2, index1,index;
FileIndexItem *item = nullptr;
const int64_t buf_size = 1000;
char buf[buf_size];
char buffer[buf_size];
int64_t pos = 0;
int64_t position = 0;
const char *buf1 = "0:2:3\n1:3:3\n2:4:7\n";
EXPECT_EQ(OB_SUCCESS, index.deserialize(buf1, strlen(buf1), pos));
EXPECT_EQ(OB_SUCCESS, index1.insert_index_item(0,2,3));
EXPECT_EQ(OB_SUCCESS, index1.insert_index_item(1,3,3));
EXPECT_EQ(OB_SUCCESS, index1.insert_index_item(2,4,7));
EXPECT_EQ(index, index1);
EXPECT_EQ(OB_ERROR_OUT_OF_RANGE, index.get_index_item(-1, item));
EXPECT_EQ(OB_SUCCESS, index.get_index_item(0, item));
EXPECT_EQ(OB_SUCCESS, index.get_index_item(1, item));
EXPECT_EQ(OB_SUCCESS, index.get_index_item(2, item));
EXPECT_EQ(OB_ERROR_OUT_OF_RANGE, index.get_index_item(3, item));
pos = 0;
index.reset();
index1.reset();
index2.reset();
const char *buf2 = "1:2:3\n4:5:6\n3:4:5\n";
EXPECT_EQ(OB_SUCCESS, index.deserialize(buf2, strlen(buf2), pos));
EXPECT_EQ(OB_ERR_UNEXPECTED, index.get_index_item(0, item));
pos = 0;
index.reset();
index1.reset();
index2.reset();
const char *buf3 = "0:1:2\n1:2:3\n";
EXPECT_EQ(OB_SUCCESS, index.deserialize(buf3, strlen(buf3), pos));
EXPECT_EQ(strlen(buf3), index.get_index_file_len());
pos = 0;
index.reset();
index1.reset();
index2.reset();
for(int i = 0; i < 1000; i++) {
index.reset();
index1.reset();
int item_cnt = abs(rand()) % 10 + 1;
for (int j = 0; j < item_cnt; j++) {
FileIndexItem item;
item.reset(j, rand(), rand());
EXPECT_EQ(OB_SUCCESS, index1.insert_index_item(item));
EXPECT_EQ(OB_SUCCESS, item.serialize(buffer, buf_size, position));
}
EXPECT_EQ(position, index1.get_serialize_size());
EXPECT_EQ(position, index1.get_index_file_len());
int64_t data_len = position;
position = 0;
EXPECT_EQ(OB_SUCCESS, index2.deserialize(buffer, data_len, position));
EXPECT_EQ(index1, index2);
EXPECT_EQ(OB_SUCCESS, index1.serialize(buf, buf_size, pos));
data_len = pos;
pos = 0;
EXPECT_EQ(OB_SUCCESS, index.deserialize(buf, data_len, pos));
EXPECT_EQ(index, index1);
pos = 0;
position = 0;
index.reset();
index1.reset();
index2.reset();
}
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_file_index.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_file_index.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2023 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.
*/
#define private public
#include "ob_log_miner_file_manager.h"
#undef private
#include "gtest/gtest.h"
namespace oceanbase
{
namespace oblogminer
{
TEST(test_ob_log_miner_file_manager, GenFileHeader)
{
ObLogMinerFileManager manager;
const int64_t path_size = 100;
char cwd_buf[path_size];
ObArenaAllocator alloc;
char *header = nullptr;
int64_t data_len = 0;
EXPECT_NE(nullptr, getcwd(cwd_buf, path_size));
char path[OB_MAX_URI_LENGTH];
char rm_dir_command[OB_MAX_URI_LENGTH];
EXPECT_EQ(OB_SUCCESS, databuff_printf(path, sizeof(path), "file://%s/logminer_output", cwd_buf));
EXPECT_EQ(OB_SUCCESS, databuff_printf(rm_dir_command, sizeof(rm_dir_command), "rm -rf %s/logminer_output", cwd_buf));
EXPECT_EQ(OB_SUCCESS,
manager.init(path, RecordFileFormat::CSV, ObLogMinerFileManager::FileMgrMode::ANALYZE));
EXPECT_EQ(OB_SUCCESS, manager.generate_data_file_header_(alloc, header, data_len));
EXPECT_STREQ(header, "TENANT_ID,TRANS_ID,PRIMARY_KEY,TENANT_NAME,DATABASE_NAME,"
"TABLE_NAME,OPERATION,OPERATION_CODE,COMMIT_SCN,COMMIT_TIMESTAMP,SQL_REDO,SQL_UNDO,ORG_CLUSTER_ID\n");
system(rm_dir_command);
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_file_manager.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_file_manager.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2023 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 "ob_log_miner_file_meta.h"
#include "gtest/gtest.h"
namespace oceanbase
{
namespace oblogminer
{
TEST(test_log_miner_file_meta, SerializeFunc)
{
ObLogMinerFileMeta file_meta, file_meta1;
const int64_t buf_len = 300;
char buf[buf_len];
int64_t pos = 0;
const char *buf1 = "MIN_COMMIT_TS=1\nMAX_COMMIT_TS=2\nDATA_LEN=3\n";
EXPECT_EQ(OB_SUCCESS, file_meta.deserialize(buf1, strlen(buf1), pos));
EXPECT_EQ(pos, strlen(buf1));
EXPECT_EQ(file_meta.range_.min_commit_ts_, 1);
EXPECT_EQ(file_meta.range_.max_commit_ts_, 2);
EXPECT_EQ(file_meta.data_length_, 3);
pos = 0;
const char *buf2 = "MIN_COMMIT_TS=1a\nMAX_COMMIT_TS=2\nDATA_LEN=3\n";
EXPECT_EQ(OB_INVALID_DATA, file_meta.deserialize(buf2, strlen(buf2), pos));
pos = 0;
const char *buf3 = "MIN_COMMIT_TS=11\nMAX_COMMIT_TS=2\nDATA_LEN=3";
EXPECT_EQ(OB_SIZE_OVERFLOW, file_meta.deserialize(buf3, strlen(buf3), pos));
pos = 0;
for (int i = 0; i < 10000; i++) {
file_meta1.data_length_ = rand();
file_meta1.range_.min_commit_ts_ = rand();
file_meta1.range_.max_commit_ts_ = rand();
EXPECT_EQ(OB_SUCCESS, file_meta1.serialize(buf, buf_len, pos));
EXPECT_EQ(pos, file_meta1.get_serialize_size());
pos = 0;
EXPECT_EQ(OB_SUCCESS, file_meta.deserialize(buf, buf_len, pos));
EXPECT_EQ(file_meta, file_meta1);
pos = 0;
}
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_file_meta.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_file_meta.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2023 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 "ob_log_miner_progress_range.h"
#include "gtest/gtest.h"
namespace oceanbase
{
namespace oblogminer
{
TEST(test_log_miner_progress_range, SerializeFunc)
{
ObLogMinerProgressRange range, range1;
const int64_t buf_len = 100;
char buf[buf_len];
int64_t pos = 0;
const char *buf1 = "MIN_COMMIT_TS=1\nMAX_COMMIT_TS=2\n";
EXPECT_EQ(OB_SUCCESS, range.deserialize(buf1, strlen(buf1), pos));
EXPECT_EQ(range.min_commit_ts_, 1);
EXPECT_EQ(range.max_commit_ts_, 2);
pos = 0;
const char *buf2 = "MIN_COMMIT_TS=aaa\nMAX_COMMIT_TS=2\n";
EXPECT_EQ(OB_INVALID_DATA, range.deserialize(buf2, strlen(buf2), pos));
pos = 0;
const char *buf3 = "MIN_COMMIT_TS:1\nMAX_COMMIT_TS:2\n";
EXPECT_EQ(OB_INVALID_DATA, range.deserialize(buf3, strlen(buf3), pos));
pos = 0;
const char *buf4 = "MIN_COMMIT_TS=1MAX_COMMIT_TS=2\n";
EXPECT_EQ(OB_INVALID_DATA, range.deserialize(buf4, strlen(buf4), pos));
pos = 0;
for (int i = 0; i < 10000; i++) {
range1.min_commit_ts_ = rand();
range1.max_commit_ts_ = rand();
EXPECT_EQ(range1.serialize(buf, buf_len, pos), OB_SUCCESS);
EXPECT_EQ(pos, range1.get_serialize_size());
pos = 0;
EXPECT_EQ(range.deserialize(buf, buf_len, pos), OB_SUCCESS);
EXPECT_EQ(range.get_serialize_size(), pos);
EXPECT_EQ(range, range1);
pos = 0;
}
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_progress_range.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_progress_range.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,795 @@
/**
* Copyright (c) 2023 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 "lib/oblog/ob_log.h"
#include "ob_log_miner_test_utils.h"
#include "gtest/gtest.h"
#define private public
#include "ob_log_miner_record.h"
#undef private
namespace oceanbase
{
namespace oblogminer
{
TEST(test_ob_log_miner_record, InitObLogMinerRecord)
{
ObArenaAllocator allocator("TestLogMnrRec");
ObLogMinerBR *br = nullptr;
ObLogMinerRecord record;
const int buf_cnt = 10;
binlogBuf *new_buf = static_cast<binlogBuf*>(allocator.alloc(sizeof(binlogBuf) * buf_cnt));
binlogBuf *old_buf = static_cast<binlogBuf*>(allocator.alloc(sizeof(binlogBuf) * buf_cnt));
for (int i = 0; i < 10; i++) {
new_buf[i].buf = static_cast<char*>(allocator.alloc(1024));
new_buf[i].buf_size = 1024;
new_buf[i].buf_used_size = 0;
old_buf[i].buf = static_cast<char*>(allocator.alloc(1024));
old_buf[i].buf_size = 1024;
old_buf[i].buf_used_size = 0;
}
record.set_allocator(&allocator);
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"t1.db1", "tbl1", 4, "id", "1", nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("t1", record.tenant_name_.ptr());
EXPECT_STREQ("db1", record.database_name_.ptr());
EXPECT_STREQ("tbl1", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db1`.`tbl1` (`id`) VALUES ('1');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db1`.`tbl1` WHERE `id`='1' LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", 8, "id", "1", "2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"name", "aaa", "bbb", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `id`='1', `name`='aaa' WHERE `id`='2' AND `name`='bbb' LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `id`='2', `name`='bbb' WHERE `id`='1' AND `name`='aaa' LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", 8, "id", nullptr , "2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"name", nullptr, "bbb", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `id`='2' AND `name`='bbb' LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`id`, `name`) VALUES ('2', 'bbb');", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDDL, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "", 4, "ddl_stmt_str", "CREATE TABLE T1(ID INT PRIMARY KEY);" , nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("CREATE TABLE T1(ID INT PRIMARY KEY);", record.redo_stmt_.ptr());
EXPECT_STREQ("/* NO SQL_UNDO GENERATED */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "gbk", 8,
"id", nullptr , "2",static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"name", nullptr, "\xB0\xC2\xD0\xC7\xB1\xB4\xCB\xB9",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `id`='2' AND `name`='\xE5\xA5\xA5\xE6\x98\x9F\xE8\xB4\x9D\xE6\x96\xAF' LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`id`, `name`) VALUES ('2', '\xE5\xA5\xA5\xE6\x98\x9F\xE8\xB4\x9D\xE6\x96\xAF');", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf16", 4, "name", nullptr, "\x59\x65\x66\x1F\x8D\x1D\x65\xAF",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `name`='\xE5\xA5\xA5\xE6\x98\x9F\xE8\xB4\x9D\xE6\x96\xAF' LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`name`) VALUES ('\xE5\xA5\xA5\xE6\x98\x9F\xE8\xB4\x9D\xE6\x96\xAF');", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "latin1", 8, "id", nullptr , "2",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"name", nullptr, "\x63\x69\xe0\x6f",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `id`='2' AND `name`='\x63\x69\xc3\xa0\x6f' LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`id`, `name`) VALUES ('2', '\x63\x69\xc3\xa0\x6f');", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "gb18030", 8,
"id", nullptr , "2",static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING),
"name", nullptr, "\x95\xcb\x95\xcb\xc6\xe4\xd2\xf5\xa3\xac\xc6\xe4\xec\x59\xf2\xb3\xf2\xb3",
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `id`='2' AND `name`='\xe6\x9b\x80\xe6\x9b\x80\xe5\x85\xb6\xe9\x98\xb4\xef\xbc\x8c\xe5\x85\xb6\xe9\x9d\x81\xe8\x99\xba\xe8\x99\xba' LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`id`, `name`) VALUES ('2', '\xe6\x9b\x80\xe6\x9b\x80\xe5\x85\xb6\xe9\x98\xb4\xef\xbc\x8c\xe5\x85\xb6\xe9\x9d\x81\xe8\x99\xba\xe8\x99\xba');", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "gb18030", 4,
"id", nullptr , "2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"id\"=2 and ROWNUM=1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"id\") VALUES (2);", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "gb18030", 4,
"id", nullptr , "2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"id\"='2' and ROWNUM=1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"id\") VALUES ('2');", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "gb18030", 4,
"id", "2" , nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_VAR_STRING));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"id\") VALUES ('2');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"id\"='2' and ROWNUM=1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "gb18030", 4,
"id", "2" , nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_BLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"id\") VALUES ('2');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE 0=DBMS_LOB.COMPARE(\"id\", '2') and ROWNUM=1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 4,
"name", "ABCD" , nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_BLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"name\") VALUES (HEXTORAW('41424344'));", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE 0=DBMS_LOB.COMPARE(\"name\", HEXTORAW('41424344')) and ROWNUM=1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "binary", 4,
"name", "ABCD" , nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_BLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`name`) VALUES (UNHEX('41424344'));", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `name`=UNHEX('41424344') LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8, "name", "ABCD" , nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING),
"col2", "XXX", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING));
br->get_br()->getTableMeta()->setUKs("name");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`name`, `col2`) VALUES ('ABCD', 'XXX');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `name`='ABCD' LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8, "name", "ABCD" , nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING),
"col2", "XXX", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING));
br->get_br()->getTableMeta()->setPKs("col2");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`name`, `col2`) VALUES ('ABCD', 'XXX');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col2`='XXX' LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12, "name", "ABCD" , nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING),
"col2", "XXX", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING),
"col3", "val3", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING));
br->get_br()->getTableMeta()->setPKs("col2,col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`name`, `col2`, `col3`) VALUES ('ABCD', 'XXX', 'val3');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col2`='XXX' AND `col3`='val3' LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12, "name", "ABCD" , nullptr,
static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING),
"col2", "XXX", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING),
"col3", "val3", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_STRING));
br->get_br()->getTableMeta()->setUKs("col2,col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`name`, `col2`, `col3`) VALUES ('ABCD', 'XXX', 'val3');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col2`='XXX' AND `col3`='val3' LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12,
"col1", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG),
"col2", "2", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG),
"col3", "3", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col2,col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`, `col3`) VALUES (1, 2, 3);", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col2`=2 AND `col3`=3 LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8,
"col1", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_BIT),
"col2", "1836032", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_BIT));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`) VALUES (b'1', b'111000000010000000000');", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col1`=b'1' AND `col2`=b'111000000010000000000' LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
}
TEST(test_ob_log_miner_record, LobTypeInMySqlMode)
{
ObArenaAllocator allocator("TestLogMnrRec");
ObLogMinerBR *br = nullptr;
ObLogMinerRecord record;
const int buf_cnt = 10;
binlogBuf *new_buf = static_cast<binlogBuf*>(allocator.alloc(sizeof(binlogBuf) * buf_cnt));
binlogBuf *old_buf = static_cast<binlogBuf*>(allocator.alloc(sizeof(binlogBuf) * buf_cnt));
for (int i = 0; i < 10; i++) {
new_buf[i].buf = static_cast<char*>(allocator.alloc(1024));
new_buf[i].buf_size = 1024;
new_buf[i].buf_used_size = 0;
old_buf[i].buf = static_cast<char*>(allocator.alloc(1024));
old_buf[i].buf_size = 1024;
old_buf[i].buf_used_size = 0;
}
record.set_allocator(&allocator);
// mysql multimode
// insert without key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8,
"col1", "{\"key\": \"value\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "POINT(0 1)", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`) VALUES ('{\"key\": \"value\"}', ST_GeomFromText('POINT(0 1)'));",record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col1`=cast('{\"key\": \"value\"}'as json) AND ST_Equals(`col2`, ST_GeomFromText('POINT(0 1)')) LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// insert with key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12,
"col1", "{\"key\": \"value\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "POINT(0 1)", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`, `col3`) VALUES ('{\"key\": \"value\"}', ST_GeomFromText('POINT(0 1)'), 1);", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col3`=1 LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value insert without key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8,
"col1", nullptr, nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "POINT(0 1)", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`) VALUES (NULL, ST_GeomFromText('POINT(0 1)'));",record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col1` IS NULL AND ST_Equals(`col2`, ST_GeomFromText('POINT(0 1)')) LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value insert with key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12,
"col1", "{\"key\": \"value\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`, `col3`) VALUES ('{\"key\": \"value\"}', NULL, 1);", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col3`=1 LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// update without key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8,
"col1", "{\"key\": \"new\"}", "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "POINT(0 1)", "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`='{\"key\": \"new\"}', `col2`=ST_GeomFromText('POINT(0 1)') WHERE `col1`=cast('{\"key\": \"old\"}'as json) AND ST_Equals(`col2`, ST_GeomFromText('POINT(2 3)')) LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`='{\"key\": \"old\"}', `col2`=ST_GeomFromText('POINT(2 3)') WHERE `col1`=cast('{\"key\": \"new\"}'as json) AND ST_Equals(`col2`, ST_GeomFromText('POINT(0 1)')) LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// update with key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12,
"col1", "{\"key\": \"new\"}", "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "POINT(0 1)", "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "1", "2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`='{\"key\": \"new\"}', `col2`=ST_GeomFromText('POINT(0 1)'), `col3`=1 WHERE `col3`=2 LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`='{\"key\": \"old\"}', `col2`=ST_GeomFromText('POINT(2 3)'), `col3`=2 WHERE `col3`=1 LIMIT 1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value update without key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8,
"col1", "{\"key\": \"new\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "POINT(0 1)", "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`='{\"key\": \"new\"}', `col2`=ST_GeomFromText('POINT(0 1)') WHERE `col1` IS NULL AND ST_Equals(`col2`, ST_GeomFromText('POINT(2 3)')) LIMIT 1;/* POTENTIALLY INACCURATE */",record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`=NULL, `col2`=ST_GeomFromText('POINT(2 3)') WHERE `col1`=cast('{\"key\": \"new\"}'as json) AND ST_Equals(`col2`, ST_GeomFromText('POINT(0 1)')) LIMIT 1;/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value update with key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12,
"col1", "{\"key\": \"new\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "POINT(0 1)", "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`='{\"key\": \"new\"}', `col2`=ST_GeomFromText('POINT(0 1)'), `col3`=1 WHERE `col3` IS NULL LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE `db2`.`tbl2` SET `col1`=NULL, `col2`=ST_GeomFromText('POINT(2 3)'), `col3`=NULL WHERE `col3`=1 LIMIT 1;/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// delete without key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8,
"col1", nullptr, "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col1`=cast('{\"key\": \"old\"}'as json) AND ST_Equals(`col2`, ST_GeomFromText('POINT(2 3)')) LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`) VALUES ('{\"key\": \"old\"}', ST_GeomFromText('POINT(2 3)'));", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// delete with key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12,
"col1", nullptr, "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", nullptr, "2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col3`=2 LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`, `col3`) VALUES ('{\"key\": \"old\"}', ST_GeomFromText('POINT(2 3)'), 2);", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value delete without key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 8,
"col1", nullptr, nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col1` IS NULL AND ST_Equals(`col2`, ST_GeomFromText('POINT(2 3)')) LIMIT 1;/* POTENTIALLY INACCURATE */",record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`) VALUES (NULL, ST_GeomFromText('POINT(2 3)'));/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value delete with key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::MYSQL,
"tenant2.db2", "tbl2", "utf8mb4", 12,
"col1", nullptr, nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, "POINT(2 3)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", nullptr, "1", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col3");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM `db2`.`tbl2` WHERE `col3`=1 LIMIT 1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO `db2`.`tbl2` (`col1`, `col2`, `col3`) VALUES (NULL, ST_GeomFromText('POINT(2 3)'), 1);/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
}
TEST(test_ob_log_miner_record, LobTypeInOracleMode)
{
ObArenaAllocator allocator("TestLogMnrRec");
ObLogMinerBR *br = nullptr;
ObLogMinerRecord record;
const int buf_cnt = 10;
binlogBuf *new_buf = static_cast<binlogBuf*>(allocator.alloc(sizeof(binlogBuf) * buf_cnt));
binlogBuf *old_buf = static_cast<binlogBuf*>(allocator.alloc(sizeof(binlogBuf) * buf_cnt));
for (int i = 0; i < 10; i++) {
new_buf[i].buf = static_cast<char*>(allocator.alloc(1024));
new_buf[i].buf_size = 1024;
new_buf[i].buf_used_size = 0;
old_buf[i].buf = static_cast<char*>(allocator.alloc(1024));
old_buf[i].buf_size = 1024;
old_buf[i].buf_used_size = 0;
}
record.set_allocator(&allocator);
// multimode
// insert without key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 16,
"col1", "{\"key\": \"value\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "SRID=NULL;POINT(0 1)", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "<a>abc</a>", nullptr, drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\") VALUES ('{\"key\": \"value\"}', SDO_GEOMETRY('POINT(0 1)', NULL), '<a>abc</a>', 'AABB1122');",record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE JSON_EQUAL(\"col1\", '{\"key\": \"value\"}') AND \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL) AND \"col3\"='<a>abc</a>' AND 0=DBMS_LOB.COMPARE(\"col4\", 'AABB1122') and ROWNUM=1;/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// insert with key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 20,
"col1", "{\"key\": \"value\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "SRID=NULL;POINT(0 1)", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "<a>abc</a>", nullptr, drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB),
"col5", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col5");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\", \"col5\") VALUES ('{\"key\": \"value\"}', SDO_GEOMETRY('POINT(0 1)', NULL), '<a>abc</a>', 'AABB1122', 1);", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"col5\"=1 and ROWNUM=1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value insert without key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 16,
"col1", nullptr, nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col2", "SRID=NULL;POINT(0 1)", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "<a>abc</a>", nullptr, drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\") VALUES (NULL, SDO_GEOMETRY('POINT(0 1)', NULL), '<a>abc</a>', 'AABB1122');",record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"col1\" IS NULL AND \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL) AND \"col3\"='<a>abc</a>' AND 0=DBMS_LOB.COMPARE(\"col4\", 'AABB1122') and ROWNUM=1;/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value insert with key
br = build_logminer_br(new_buf, old_buf, EINSERT, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 20,
"col1", "{\"key\": \"value\"}", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "<a>abc</a>", nullptr, drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB),
"col5", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col5");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\", \"col5\") VALUES ('{\"key\": \"value\"}', NULL, '<a>abc</a>', 'AABB1122', 1);", record.redo_stmt_.ptr());
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"col5\"=1 and ROWNUM=1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// update without key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 16,
"col1", "{\"key\": \"new\"}", "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "SRID=NULL;POINT(0 1)", "SRID=NULL;POINT(2 1)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "<a>abc</a>", "<a>cba</a>", drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", "1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"new\"}', \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL),"
" \"col3\"='<a>abc</a>', \"col4\"='AABB1122' WHERE JSON_EQUAL(\"col1\", '{\"key\": \"old\"}') AND "
"\"col2\"=SDO_GEOMETRY('POINT(2 1)', NULL) AND \"col3\"='<a>cba</a>' AND 0=DBMS_LOB.COMPARE(\"col4\", '1122')"
" AND ROWNUM=1;/* POTENTIALLY INACCURATE */", record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"old\"}', \"col2\"=SDO_GEOMETRY('POINT(2 1)', NULL),"
" \"col3\"='<a>cba</a>', \"col4\"='1122' WHERE JSON_EQUAL(\"col1\", '{\"key\": \"new\"}') AND "
"\"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL) AND \"col3\"='<a>abc</a>' AND 0=DBMS_LOB.COMPARE(\"col4\", 'AABB1122')"
" AND ROWNUM=1;/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// update with key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 20,
"col1", "{\"key\": \"new\"}", "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "SRID=NULL;POINT(0 1)", "SRID=NULL;POINT(2 1)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "<a>abc</a>", "<a>cba</a>", drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", "1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB),
"col5", "1", "2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col5");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"new\"}', \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL), \"col3\"='<a>abc</a>', \"col4\"='AABB1122', \"col5\"=1 WHERE \"col5\"=2 AND ROWNUM=1;", record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"old\"}', \"col2\"=SDO_GEOMETRY('POINT(2 1)', NULL), \"col3\"='<a>cba</a>', \"col4\"='1122', \"col5\"=2 WHERE \"col5\"=1 AND ROWNUM=1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value update without key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 16,
"col1", "{\"key\": \"new\"}", "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "SRID=NULL;POINT(0 1)", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", nullptr, nullptr, drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", "1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"new\"}', \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL),"
" \"col3\"=NULL, \"col4\"='AABB1122' WHERE JSON_EQUAL(\"col1\", '{\"key\": \"old\"}') AND \"col2\" IS NULL AND "
"\"col3\" IS NULL AND 0=DBMS_LOB.COMPARE(\"col4\", '1122') AND ROWNUM=1;/* POTENTIALLY INACCURATE */",record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"old\"}', \"col2\"=NULL, \"col3\"=NULL,"
" \"col4\"='1122' WHERE JSON_EQUAL(\"col1\", '{\"key\": \"new\"}') AND \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL)"
" AND \"col3\" IS NULL AND 0=DBMS_LOB.COMPARE(\"col4\", 'AABB1122') AND ROWNUM=1;/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value update with key
br = build_logminer_br(new_buf, old_buf, EUPDATE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 20,
"col1", "{\"key\": \"new\"}", "{\"key\": \"old\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", "SRID=NULL;POINT(0 1)", "SRID=NULL;POINT(2 1)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", "<a>abc</a>", "<a>cba</a>", drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", "AABB1122", "1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB),
"col5", "1", nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col5");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"new\"}', \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL), \"col3\"='<a>abc</a>', \"col4\"='AABB1122', \"col5\"=1 WHERE \"col5\" IS NULL AND ROWNUM=1;", record.redo_stmt_.ptr());
EXPECT_STREQ("UPDATE \"db2\".\"tbl2\" SET \"col1\"='{\"key\": \"old\"}', \"col2\"=SDO_GEOMETRY('POINT(2 1)', NULL), \"col3\"='<a>cba</a>', \"col4\"='1122', \"col5\"=NULL WHERE \"col5\"=1 AND ROWNUM=1;", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// delete without key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 16,
"col1", nullptr, "{\"key\": \"value\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, "SRID=NULL;POINT(0 1)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", nullptr, "<a>abc</a>", drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", nullptr, "AABB1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE JSON_EQUAL(\"col1\", '{\"key\": \"value\"}') AND \"col2\"=SDO_GEOMETRY('POINT(0 1)', NULL) AND \"col3\"='<a>abc</a>' AND 0=DBMS_LOB.COMPARE(\"col4\", 'AABB1122') and ROWNUM=1;/* POTENTIALLY INACCURATE */", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\") VALUES ('{\"key\": \"value\"}', SDO_GEOMETRY('POINT(0 1)', NULL), '<a>abc</a>', 'AABB1122');", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// delete with key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 20,
"col1", nullptr, "{\"key\": \"value\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, "SRID=NULL;POINT(0 1)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", nullptr, "<a>abc</a>", drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", nullptr, "AABB1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB),
"col5", nullptr, "2", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col5");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"col5\"=2 and ROWNUM=1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\", \"col5\") VALUES ('{\"key\": \"value\"}', SDO_GEOMETRY('POINT(0 1)', NULL), '<a>abc</a>', 'AABB1122', 2);", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value delete without key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 16,
"col1", nullptr, "{\"key\": \"value\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, nullptr, static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", nullptr, "<a>abc</a>", drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", nullptr, "AABB1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB));
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE JSON_EQUAL(\"col1\", '{\"key\": \"value\"}') AND \"col2\" IS NULL AND \"col3\"='<a>abc</a>' AND 0=DBMS_LOB.COMPARE(\"col4\", 'AABB1122') and ROWNUM=1;/* POTENTIALLY INACCURATE */",record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\") VALUES ('{\"key\": \"value\"}', NULL, '<a>abc</a>', 'AABB1122');/* POTENTIALLY INACCURATE */", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
// null value delete with key
br = build_logminer_br(new_buf, old_buf, EDELETE, lib::Worker::CompatMode::ORACLE,
"tenant2.db2", "tbl2", "utf8mb4", 20,
"col1", nullptr, "{\"key\": \"value\"}", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_JSON),
"col2", nullptr, "SRID=NULL;POINT(0 1)", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_GEOMETRY),
"col3", nullptr, "<a>abc</a>", drcmsg_field_types::DRCMSG_TYPE_ORA_XML,
"col4", nullptr, "AABB1122", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_ORA_CLOB),
"col5", nullptr, "1", static_cast<int>(obmysql::EMySQLFieldType::MYSQL_TYPE_LONG));
br->get_br()->getTableMeta()->setUKs("col5");
EXPECT_EQ(OB_SUCCESS, record.init(*br));
EXPECT_STREQ("tenant2", record.tenant_name_.ptr());
EXPECT_STREQ("db2", record.database_name_.ptr());
EXPECT_STREQ("tbl2", record.table_name_.ptr());
EXPECT_EQ(OB_SUCCESS, record.build_stmts(*br));
EXPECT_STREQ("DELETE FROM \"db2\".\"tbl2\" WHERE \"col5\"=1 and ROWNUM=1;", record.redo_stmt_.ptr());
EXPECT_STREQ("INSERT INTO \"db2\".\"tbl2\" (\"col1\", \"col2\", \"col3\", \"col4\", \"col5\") VALUES ('{\"key\": \"value\"}', SDO_GEOMETRY('POINT(0 1)', NULL), '<a>abc</a>', 'AABB1122', 1);", record.undo_stmt_.ptr());
destroy_miner_br(br);
record.destroy();
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_record.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_record.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,313 @@
/**
* Copyright (c) 2023 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 "ob_log_miner_timezone_getter.h"
#include "ob_log_miner_test_utils.h"
#include "gtest/gtest.h"
#define private public
#include "ob_log_miner_record_converter.h"
#undef private
namespace oceanbase
{
namespace oblogminer
{
TEST(test_ob_log_miner_record_converter, CsvConverterWriteType)
{
ObLogMinerRecordCsvConverter converter;
ObConcurrentFIFOAllocator alloc;
ObStringBuffer str_buf(&alloc);
EXPECT_EQ(OB_SUCCESS, alloc.init(1 << 20, 1 << 20, 1 << 13));
EXPECT_EQ(OB_SUCCESS, converter.write_csv_string_escape_("'aaaa\"\"bbbbb'", str_buf));
EXPECT_STREQ("\"'aaaa\"\"\"\"bbbbb'\"", str_buf.ptr());
str_buf.reset();
}
TEST(test_ob_log_miner_record_converter, JsonConverterWriteType)
{
ObLogMinerRecordJsonConverter converter;
ObConcurrentFIFOAllocator alloc;
ObStringBuffer str_buf(&alloc);
EXPECT_EQ(OB_SUCCESS, alloc.init(1 << 20, 1 << 20, 1 << 13));
EXPECT_EQ(OB_SUCCESS, converter.write_json_key_("aaa", str_buf));
EXPECT_STREQ("\"aaa\":", str_buf.ptr());
str_buf.reset();
EXPECT_EQ(OB_SUCCESS, converter.write_json_string_escape_("'aaaa\"\"bbbbb'", str_buf));
EXPECT_STREQ("\"'aaaa\\\"\\\"bbbbb'\"", str_buf.ptr());
str_buf.reset();
EXPECT_EQ(OB_SUCCESS, converter.write_json_string_escape_("\n\b\t", str_buf));
EXPECT_STREQ("\"\\n\\b\\t\"", str_buf.ptr());
str_buf.reset();
}
TEST(test_ob_log_miner_record_converter, CsvConverterWriteRecord)
{
ObLogMinerRecordCsvConverter converter;
ObConcurrentFIFOAllocator alloc;
EXPECT_EQ(OB_SUCCESS, LOGMINER_TZ.set_timezone("+8:00"));
bool is_written = false;
EXPECT_EQ(OB_SUCCESS, alloc.init(1 << 20, 1 << 20, 1 << 13));
ObStringBuffer str_buf(&alloc);
ObLogMinerRecord *rec = nullptr;
const char *pkarr1[] = {"aaa", "bbb"};
const char *ukarr1[] = {"ccc"};
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest1", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EINSERT, 1645539742222222,
"INSERT INTO \"test\".\"sbtest1\"(\"aaa\",\"bbb\",\"ccc\") VALUES('1','2','3');",
"DELETE FROM \"test\".\"sbtest1\" WHERE \"aaa\"='1' and \"bbb\"='2' and \"ccc\"='3';");
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"1002,345,\"aaa/bbb\",\"test_tenant\",\"test\",\"sbtest1\",\"INSERT\",1,1645539742222222000,\"2022-02-22 22:22:22.222222\","
"\"INSERT INTO \"\"test\"\".\"\"sbtest1\"\"(\"\"aaa\"\",\"\"bbb\"\",\"\"ccc\"\") VALUES('1','2','3');\","
"\"DELETE FROM \"\"test\"\".\"\"sbtest1\"\" WHERE \"\"aaa\"\"='1' and \"\"bbb\"\"='2' and \"\"ccc\"\"='3';\","
"1\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest1", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EUPDATE, 0,
"UPDATE \"test\".\"sbtest1\" SET \"aaa\" = '1', \"bbb\" = '2', \"ccc\" = '3' WHERE "
"\"aaa\" = '4' AND \"bbb\" = '5' and \"ccc\" = '6' LIMIT 1;",
"UPDATE \"test\".\"sbtest1\" SET \"aaa\" = '4', \"bbb\" = '5', \"ccc\" = '6' WHERE "
"\"aaa\" = '1' AND \"bbb\" = '2' and \"ccc\" = '3' LIMIT 1;");
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"1002,345,\"aaa/bbb\",\"test_tenant\",\"test\",\"sbtest1\",\"UPDATE\",2,0,\"1970-01-01 08:00:00.000000\","
"\"UPDATE \"\"test\"\".\"\"sbtest1\"\" SET \"\"aaa\"\" = '1', \"\"bbb\"\" = '2', \"\"ccc\"\" = '3' WHERE "
"\"\"aaa\"\" = '4' AND \"\"bbb\"\" = '5' and \"\"ccc\"\" = '6' LIMIT 1;\","
"\"UPDATE \"\"test\"\".\"\"sbtest1\"\" SET \"\"aaa\"\" = '4', \"\"bbb\"\" = '5', \"\"ccc\"\" = '6' WHERE "
"\"\"aaa\"\" = '1' AND \"\"bbb\"\" = '2' and \"\"ccc\"\" = '3' LIMIT 1;\","
"1\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "", 345, nullptr,0,
nullptr, 0, nullptr, EDDL, 4611686018427387,
"CREATE TABLE t1(id INT, name TEXT);",
nullptr);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"1002,345,\"\",\"test_tenant\",\"test\",\"\",\"DDL\",4,4611686018427387000,\"2116-02-21 07:53:38.427387\","
"\"CREATE TABLE t1(id INT, name TEXT);\","
"\"\","
"1\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest1", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EDELETE, 0,
"DELETE FROM \"test\".\"sbtest1\" WHERE \"aaa\"='1' and \"bbb\"='2' and \"ccc\"='3';",
"INSERT INTO \"test\".\"sbtest1\"(\"aaa\",\"bbb\",\"ccc\") VALUES('1','2','3');"
);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"1002,345,\"aaa/bbb\",\"test_tenant\",\"test\",\"sbtest1\",\"DELETE\",3,0,\"1970-01-01 08:00:00.000000\","
"\"DELETE FROM \"\"test\"\".\"\"sbtest1\"\" WHERE \"\"aaa\"\"='1' and \"\"bbb\"\"='2' and \"\"ccc\"\"='3';\","
"\"INSERT INTO \"\"test\"\".\"\"sbtest1\"\"(\"\"aaa\"\",\"\"bbb\"\",\"\"ccc\"\") VALUES('1','2','3');\","
"1\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
}
TEST(test_ob_log_miner_record_converter, RedoSqlConverterWriteRecord)
{
ObLogMinerRecordRedoSqlConverter converter;
ObConcurrentFIFOAllocator alloc;
bool is_written = false;
EXPECT_EQ(OB_SUCCESS, alloc.init(1 << 20, 1 << 20, 1 << 13));
ObStringBuffer str_buf(&alloc);
ObLogMinerRecord *rec = nullptr;
const char *pkarr1[] = {"aaa", "bbb"};
const char *ukarr1[] = {"ccc"};
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest1", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EINSERT, 1645539742222222,
"INSERT INTO \"test\".\"sbtest1\"(\"aaa\",\"bbb\",\"ccc\") VALUES('1','2','3');",
"DELETE FROM \"test\".\"sbtest1\" WHERE \"aaa\"='1' and \"bbb\"='2' and \"ccc\"='3';");
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"INSERT INTO \"test\".\"sbtest1\"(\"aaa\",\"bbb\",\"ccc\") VALUES('1','2','3');\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "", 345, nullptr,0,
nullptr, 0, nullptr, EDDL, 4611686018427387,
"CREATE TABLE t1(id INT, name TEXT);",
nullptr);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"CREATE TABLE t1(id INT, name TEXT);\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "", 345, nullptr,0,
nullptr, 0, nullptr, EBEGIN, 4611686018427387,
nullptr,
nullptr);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(false, is_written);
destroy_miner_record(rec);
}
TEST(test_ob_log_miner_record_converter, UndoSqlConverterWriteRecord)
{
ObLogMinerRecordUndoSqlConverter converter;
ObConcurrentFIFOAllocator alloc;
bool is_written = false;
EXPECT_EQ(OB_SUCCESS, alloc.init(1 << 20, 1 << 20, 1 << 13));
ObStringBuffer str_buf(&alloc);
ObLogMinerRecord *rec = nullptr;
const char *pkarr1[] = {"aaa", "bbb"};
const char *ukarr1[] = {"ccc"};
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest1", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EINSERT, 1645539742222222,
"INSERT INTO \"test\".\"sbtest1\"(\"aaa\",\"bbb\",\"ccc\") VALUES('1','2','3');",
"DELETE FROM \"test\".\"sbtest1\" WHERE \"aaa\"='1' and \"bbb\"='2' and \"ccc\"='3';");
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"DELETE FROM \"test\".\"sbtest1\" WHERE \"aaa\"='1' and \"bbb\"='2' and \"ccc\"='3';\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "", 345, nullptr, 0,
nullptr, 0, nullptr, EDDL, 4611686018427387,
"CREATE TABLE t1(id INT, name TEXT);",
nullptr);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(false, is_written);
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "", 345, nullptr,0,
nullptr, 0, nullptr, EBEGIN, 4611686018427387,
nullptr,
nullptr);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(false, is_written);
destroy_miner_record(rec);
}
TEST(test_ob_log_miner_record_converter, JsonConverterWriteRecord)
{
ObLogMinerRecordJsonConverter converter;
ObConcurrentFIFOAllocator alloc;
EXPECT_EQ(OB_SUCCESS, LOGMINER_TZ.set_timezone("+8:00"));
bool is_written = false;
EXPECT_EQ(OB_SUCCESS, alloc.init(1 << 20, 1 << 20, 1 << 13));
ObStringBuffer str_buf(&alloc);
ObLogMinerRecord *rec = nullptr;
const char *pkarr1[] = {"aaa", "bbb"};
const char *ukarr1[] = {"ccc"};
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest1", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EINSERT, 1645539742222222,
"INSERT INTO `test`.`sbtest1` (`aaa`, `bbb`, `ccc`) VALUES ('1', '2', '3');",
"DELETE FROM `test`.`sbtest1` WHERE `aaa`='1' AND `bbb`='2' AND `ccc`='3' LIMIT 1;");
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"{\"TENANT_ID\":1002,\"TRANS_ID\":345,\"PRIMARY_KEY\":\"aaa/bbb\",\"TENANT_NAME\":\"test_tenant\",\"DATABASE_NAME\":\"test\","
"\"TABLE_NAME\":\"sbtest1\",\"OPERATION\":\"INSERT\",\"OPERATION_CODE\":1,\"COMMIT_SCN\":1645539742222222000,"
"\"COMMIT_TIMESTAMP\":\"2022-02-22 22:22:22.222222\","
"\"SQL_REDO\":\"INSERT INTO `test`.`sbtest1` (`aaa`, `bbb`, `ccc`) VALUES ('1', '2', '3');\","
"\"SQL_UNDO\":\"DELETE FROM `test`.`sbtest1` WHERE `aaa`='1' AND `bbb`='2' AND `ccc`='3' LIMIT 1;\","
"\"ORG_CLUSTER_ID\":1}\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest2", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EUPDATE, 0,
"UPDATE `test`.`sbtest2` SET `aaa`='44', `bbb`='55', `ccc`='66' WHERE `aaa`='11' AND `bbb`='22' AND `ccc`='33' LIMIT 1;",
"UPDATE `test`.`sbtest2` SET `aaa`='11', `bbb`='22', `ccc`='33' WHERE `aaa`='44' AND `bbb`='55' AND `ccc`='66' LIMIT 1;");
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"{\"TENANT_ID\":1002,\"TRANS_ID\":345,\"PRIMARY_KEY\":\"aaa/bbb\",\"TENANT_NAME\":\"test_tenant\","
"\"DATABASE_NAME\":\"test\",\"TABLE_NAME\":\"sbtest2\",\"OPERATION\":\"UPDATE\",\"OPERATION_CODE\":2,"
"\"COMMIT_SCN\":0,\"COMMIT_TIMESTAMP\":\"1970-01-01 08:00:00.000000\","
"\"SQL_REDO\":\"UPDATE `test`.`sbtest2` SET `aaa`='44', `bbb`='55', `ccc`='66' WHERE `aaa`='11' AND `bbb`='22' AND `ccc`='33' LIMIT 1;\","
"\"SQL_UNDO\":\"UPDATE `test`.`sbtest2` SET `aaa`='11', `bbb`='22', `ccc`='33' WHERE `aaa`='44' AND `bbb`='55' AND `ccc`='66' LIMIT 1;\","
"\"ORG_CLUSTER_ID\":1}\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "", 345, nullptr,0,
nullptr, 0, nullptr, EDDL, 4611686018427387,
"CREATE TABLE `sbtest2` (\n `aaa` varchar(100) NOT NULL,\n `bbb` varchar(100) NOT NULL,\n `ccc` varchar(100) DEFAULT NULL\n);",
nullptr);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"{\"TENANT_ID\":1002,\"TRANS_ID\":345,\"PRIMARY_KEY\":\"\",\"TENANT_NAME\":\"test_tenant\",\"DATABASE_NAME\":\"test\","
"\"TABLE_NAME\":\"\",\"OPERATION\":\"DDL\",\"OPERATION_CODE\":4,\"COMMIT_SCN\":4611686018427387000,\"COMMIT_TIMESTAMP\":\"2116-02-21 07:53:38.427387\","
"\"SQL_REDO\":\"CREATE TABLE `sbtest2` (\\n `aaa` varchar(100) NOT NULL,\\n `bbb` varchar(100) NOT NULL,\\n `ccc` varchar(100) DEFAULT NULL\\n);\",\"SQL_UNDO\":\"\","
"\"ORG_CLUSTER_ID\":1}\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
rec = build_logminer_record(alloc, lib::Worker::CompatMode::MYSQL,
1002, 1, "test_tenant", "test", "sbtest1", 345, pkarr1, sizeof(pkarr1)/ sizeof(const char*),
ukarr1, sizeof(ukarr1)/sizeof(const char*), "a/b/c/d/e", EDELETE, 0,
"DELETE FROM `test`.`sbtest1` WHERE `aaa`='1' AND `bbb`='2' AND `ccc`='3' LIMIT 1;",
"INSERT INTO `test`.`sbtest1` (`aaa`, `bbb`, `ccc`) VALUES ('1', '2', '3');"
);
is_written = false;
EXPECT_EQ(OB_SUCCESS, converter.write_record(*rec, str_buf, is_written));
EXPECT_EQ(true, is_written);
EXPECT_STREQ(
"{\"TENANT_ID\":1002,\"TRANS_ID\":345,\"PRIMARY_KEY\":\"aaa/bbb\",\"TENANT_NAME\":\"test_tenant\",\"DATABASE_NAME\":\"test\","
"\"TABLE_NAME\":\"sbtest1\",\"OPERATION\":\"DELETE\",\"OPERATION_CODE\":3,\"COMMIT_SCN\":0,"
"\"COMMIT_TIMESTAMP\":\"1970-01-01 08:00:00.000000\","
"\"SQL_REDO\":\"DELETE FROM `test`.`sbtest1` WHERE `aaa`='1' AND `bbb`='2' AND `ccc`='3' LIMIT 1;\","
"\"SQL_UNDO\":\"INSERT INTO `test`.`sbtest1` (`aaa`, `bbb`, `ccc`) VALUES ('1', '2', '3');\","
"\"ORG_CLUSTER_ID\":1}\n", str_buf.ptr());
str_buf.reset();
destroy_miner_record(rec);
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_record_converter.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_record_converter.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,310 @@
/**
* Copyright (c) 2023 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 "lib/allocator/ob_concurrent_fifo_allocator.h" //ObConcurrentFIFOAllocator
#include "lib/allocator/page_arena.h"
#include "ob_log_miner_utils.h"
#include "gtest/gtest.h"
namespace oceanbase
{
namespace oblogminer
{
TEST(test_ob_log_miner_utils, ObLogMinerDeepCopyCstring)
{
const char *archive_dest = "archive_dest";
char *val = nullptr;
ObArenaAllocator alloc;
EXPECT_EQ(OB_SUCCESS, deep_copy_cstring(alloc, archive_dest, val));
EXPECT_STREQ(archive_dest, val);
EXPECT_EQ(OB_ERR_UNEXPECTED, deep_copy_cstring(alloc, archive_dest, val));
val = nullptr;
EXPECT_EQ(OB_SUCCESS, deep_copy_cstring(alloc, nullptr, val));
EXPECT_EQ(nullptr, val);
EXPECT_EQ(OB_SUCCESS, deep_copy_cstring(alloc, "", val));
EXPECT_STREQ("", val);
}
TEST(test_ob_log_miner_utils, ObLogMinerUtilParseLineString)
{
const char *key_str = "PROGRESS";
const char *buf = "PROGRESS=abcde\n";
int64_t pos = 0;
ObString value;
ObArenaAllocator alloc;
char *cstr = nullptr;
EXPECT_EQ(OB_SUCCESS, parse_line(key_str, buf, strlen(buf), pos, value));
EXPECT_EQ(value, ObString("abcde"));
EXPECT_EQ(pos, strlen(buf));
pos = 0;
key_str = "CUR_FILE_ID";
EXPECT_EQ(OB_INVALID_DATA, parse_line(key_str, buf, strlen(buf), pos, value));
buf = "CUR_FILE_ID=001\n";
pos = 0;
EXPECT_EQ(OB_SUCCESS, parse_line(key_str, buf, strlen(buf), pos, value));
EXPECT_EQ(ObString("001"), value);
EXPECT_EQ(pos, strlen(buf));
pos = 0;
key_str = "kkkk";
buf = "kkkk=llll\n";
EXPECT_EQ(OB_SUCCESS, parse_line(key_str, buf, strlen(buf), pos, alloc, cstr));
EXPECT_STREQ("llll", cstr);
const char *charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwlyz_-.";
const int64_t charset_len = strlen(charset);
char data_buf[100];
char key[30];
char val[30];
for (int i = 0; i < 10000; i++) {
int64_t kv_len = abs(rand()) % (sizeof(key)-1) + 1;
pos = 0;
for (int j = 0; j < kv_len; j++) {
int k = rand() % charset_len;
int v = rand() % charset_len;
key[j] = charset[k];
val[j] = charset[v];
}
key[kv_len] = '\0';
val[kv_len] = '\0';
snprintf(data_buf, 100, "%s=%s\n", key, val);
EXPECT_EQ(OB_SUCCESS, parse_line(key, data_buf, sizeof(data_buf), pos, value));
EXPECT_EQ(ObString(val), value);
pos = 0;
EXPECT_EQ(OB_SUCCESS, parse_line(key, data_buf, sizeof(data_buf), pos, alloc, cstr));
EXPECT_STREQ(val, cstr);
}
}
TEST(test_ob_log_miner_utils, ObLogMinerUtilsStrToLL)
{
int64_t num = 0;
EXPECT_EQ(OB_SUCCESS, logminer_str2ll("111222", num));
EXPECT_EQ(111222, num);
EXPECT_EQ(OB_INVALID_ARGUMENT, logminer_str2ll("9223372036854775808", num));
EXPECT_EQ(OB_INVALID_ARGUMENT, logminer_str2ll("-9223372036854775809", num));
EXPECT_EQ(OB_INVALID_ARGUMENT, logminer_str2ll(nullptr, num));
EXPECT_EQ(OB_SUCCESS, logminer_str2ll("12345aaa", num));
EXPECT_EQ(12345, num);
const char *timestr = "1688473439\n";
char *end_ptr = nullptr;
EXPECT_EQ(OB_SUCCESS, logminer_str2ll(timestr, end_ptr, num));
EXPECT_EQ(1688473439, num);
EXPECT_EQ(end_ptr, timestr + strlen(timestr)-1);
timestr = "aaaaa";
EXPECT_EQ(OB_INVALID_ARGUMENT, logminer_str2ll(timestr, end_ptr, num));
EXPECT_EQ(end_ptr, timestr);
}
TEST(test_ob_log_miner_utils, ObLogMinerUtilsParseLine)
{
const char *key_str = "PROGRESS";
const char *buf = "PROGRESS=1234567\n";
int64_t pos = 0;
int64_t data = 0;
EXPECT_EQ(OB_SUCCESS, parse_line(key_str, buf, strlen(buf), pos, data));
EXPECT_EQ(data, 1234567);
EXPECT_EQ(pos, strlen(buf));
pos = 0;
key_str = "CUR_FILE_ID";
EXPECT_EQ(OB_INVALID_DATA, parse_line(key_str, buf, strlen(buf), pos, data));
buf = "CUR_FILE_ID=001\n";
pos = 0;
EXPECT_EQ(OB_SUCCESS, parse_line(key_str, buf, strlen(buf), pos, data));
EXPECT_EQ(1, data);
EXPECT_EQ(pos, strlen(buf));
buf = "CUR_FILE_ID:1\n";
pos = 0;
EXPECT_EQ(OB_INVALID_DATA, parse_line(key_str, buf, strlen(buf), pos, data));
buf = "CUR_FILE_ID=1";
pos = 0;
EXPECT_EQ(OB_SIZE_OVERFLOW, parse_line(key_str, buf, strlen(buf), pos, data));
const char *charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwlyz_-.";
const int64_t charset_len = strlen(charset);
char data_buf[100];
char key[30];
for (int i = 0; i < 10000; i++) {
int64_t key_len = abs(rand()) % (sizeof(key)-1) + 1;
pos = 0;
for (int j = 0; j < key_len; j++) {
int k = rand() % charset_len;
key[j] = charset[k];
}
key[key_len] = '\0';
int64_t val = rand();
snprintf(data_buf, 100, "%s=%ld\n", key, val);
EXPECT_EQ(OB_SUCCESS, parse_line(key, data_buf, sizeof(data_buf), pos, data));
EXPECT_EQ(val, data);
}
}
TEST(test_ob_log_miner_utils, ParseDigit)
{
int64_t pos = 0;
int64_t data = 0;
const char *buf = "12345667";
EXPECT_EQ(OB_SUCCESS, parse_int(buf, strlen(buf), pos, data));
EXPECT_EQ(data, 12345667);
EXPECT_EQ(pos, strlen(buf));
pos = 0; data = 0;
const char buf0[] = {'1','2','3','4','5'};
EXPECT_EQ(OB_SUCCESS, parse_int(buf0, sizeof(buf0), pos, data));
EXPECT_EQ(data, 12345);
EXPECT_EQ(pos, sizeof(buf0));
pos = 0; data = 0;
const char *buf2 = " +1234567aaa";
EXPECT_EQ(OB_SUCCESS, parse_int(buf2, strlen(buf2), pos, data));
EXPECT_EQ(data, 1234567);
EXPECT_EQ(pos, 10);
pos = 0; data = 0;
const char *buf3 = " -1234567aaa";
EXPECT_EQ(OB_SUCCESS, parse_int(buf3, strlen(buf3), pos, data));
EXPECT_EQ(data, -1234567);
EXPECT_EQ(pos, 10);
pos = 0; data = 0;
const char *buf4 = "9223372036854775807";
EXPECT_EQ(OB_SUCCESS, parse_int(buf4, strlen(buf4), pos, data));
EXPECT_EQ(data, 9223372036854775807LL);
EXPECT_EQ(pos, strlen(buf4));
pos = 0; data = 0;
const char *buf5 = "-9223372036854775808";
EXPECT_EQ(OB_SUCCESS, parse_int(buf5, strlen(buf5), pos, data));
EXPECT_EQ(data, -9223372036854775807LL - 1);
EXPECT_EQ(pos, strlen(buf5));
pos = 0; data = 0;
const char *buf6 = "9223372036854775808";
EXPECT_EQ(OB_SIZE_OVERFLOW, parse_int(buf6, strlen(buf6), pos, data));
pos = 0; data = 0;
const char *buf7 = "-9223372036854775809";
EXPECT_EQ(OB_SIZE_OVERFLOW, parse_int(buf7, strlen(buf7), pos, data));
pos = 0; data = 0;
const char *buf_invalid = "+-111";
EXPECT_EQ(OB_INVALID_DATA, parse_int(buf_invalid, strlen(buf_invalid), pos, data));
pos = 0; data = 0;
buf_invalid = " + 111";
EXPECT_EQ(OB_INVALID_DATA, parse_int(buf_invalid, strlen(buf_invalid), pos, data));
pos = 0; data = 0;
buf_invalid = " a111";
EXPECT_EQ(OB_INVALID_DATA, parse_int(buf_invalid, strlen(buf_invalid), pos, data));
pos = 0; data = 0;
char buf_tmp[100];
for (int i = 0; i < 10000; i++) {
pos = 0;
int64_t data1 = rand();
EXPECT_EQ(OB_SUCCESS, databuff_printf(buf_tmp, 100, pos, "CUR_FILE_ID="));
int64_t tmp_pos = pos;
EXPECT_EQ(OB_SUCCESS, databuff_printf(buf_tmp, 100, pos, "%ld\n", data1));
EXPECT_EQ(OB_SUCCESS, parse_int(buf_tmp, 100, tmp_pos, data));
EXPECT_EQ(data, data1);
}
}
TEST(test_ob_log_miner_utils, ExpectToken)
{
const char *buf = "MIN_COMMIT_TS=1";
int64_t pos = 0;
EXPECT_EQ(OB_SUCCESS, expect_token(buf, strlen(buf), pos, "MIN_COMMIT_TS"));
EXPECT_EQ(pos, 13);
EXPECT_EQ(OB_SUCCESS, expect_token(buf, strlen(buf), pos, "="));
EXPECT_EQ(pos, 14);
EXPECT_EQ(OB_SUCCESS, expect_token(buf, strlen(buf), pos, "1"));
EXPECT_EQ(pos, 15);
pos = 0;
buf = "abcdefghxxxx";
EXPECT_EQ(OB_SIZE_OVERFLOW, expect_token(buf, strlen(buf), pos, "abcdefghxxxxx"));
pos = 0;
EXPECT_EQ(OB_SUCCESS, expect_token(buf, strlen(buf), pos, "abcdefgh"));
EXPECT_EQ(pos, 8);
EXPECT_EQ(OB_SIZE_OVERFLOW, expect_token(buf, strlen(buf), pos, "xxxxx"));
pos = 8;
EXPECT_EQ(OB_SUCCESS, expect_token(buf, strlen(buf), pos, "xxxx"));
pos = 0;
buf = "CUR_FILE_ID=1\n";
EXPECT_EQ(OB_INVALID_DATA, expect_token(buf, strlen(buf), pos, "MAX_FILE_ID"));
pos = 0;
EXPECT_EQ(OB_SUCCESS, expect_token(buf, strlen(buf), pos, "CUR_FILE_ID"));
EXPECT_EQ(pos, 11);
EXPECT_EQ(OB_INVALID_DATA, expect_token(buf, strlen(buf), pos, "1"));
pos = 11;
EXPECT_EQ(OB_SUCCESS, expect_token(buf, strlen(buf), pos, "=1\n"));
EXPECT_EQ(pos, 14);
pos = 0;
}
TEST(test_ob_log_miner_utils, ConvertUintToBit)
{
uint64_t val = 0;
ObArenaAllocator tmp_alloc;
ObStringBuffer bit_str(&tmp_alloc);
EXPECT_EQ(OB_SUCCESS, uint_to_bit(val, bit_str));
EXPECT_STREQ("0", bit_str.ptr());
bit_str.reset();
val = 1;
EXPECT_EQ(OB_SUCCESS, uint_to_bit(val, bit_str));
EXPECT_STREQ("1", bit_str.ptr());
bit_str.reset();
val = 6;
EXPECT_EQ(OB_SUCCESS, uint_to_bit(val, bit_str));
EXPECT_STREQ("110", bit_str.ptr());
bit_str.reset();
val = 1836032;
EXPECT_EQ(OB_SUCCESS, uint_to_bit(val, bit_str));
EXPECT_STREQ("111000000010000000000", bit_str.ptr());
bit_str.reset();
val = 183848324234;
EXPECT_EQ(OB_SUCCESS, uint_to_bit(val, bit_str));
EXPECT_STREQ("10101011001110001101101100110010001010", bit_str.ptr());
bit_str.reset();
}
TEST(test_ob_log_miner_utils, ObLogMinerUtilsWriteRecord)
{
ObConcurrentFIFOAllocator alloc;
ObStringBuffer str_buf(&alloc);
KeyArray key_arr;
EXPECT_EQ(OB_SUCCESS, alloc.init(1 << 20, 1 << 20, 1 << 13));
EXPECT_EQ(key_arr.push_back("aaa"), OB_SUCCESS);
EXPECT_EQ(key_arr.push_back("bbb"), OB_SUCCESS);
EXPECT_EQ(key_arr.push_back("ccc"), OB_SUCCESS);
EXPECT_EQ(OB_SUCCESS, write_keys(key_arr, str_buf));
EXPECT_STREQ("\"aaa/bbb/ccc\"", str_buf.ptr());
str_buf.reset();
EXPECT_EQ(OB_SUCCESS, write_signed_number(-12345, str_buf));
EXPECT_STREQ("-12345", str_buf.ptr());
str_buf.reset();
EXPECT_EQ(OB_SUCCESS, write_unsigned_number(1111, str_buf));
EXPECT_STREQ("1111", str_buf.ptr());
str_buf.reset();
EXPECT_EQ(OB_SUCCESS, write_string_no_escape("aaaaabbbb'", str_buf));
EXPECT_STREQ("\"aaaaabbbb'\"", str_buf.ptr());
str_buf.reset();
}
}
}
int main(int argc, char **argv)
{
// testing::FLAGS_gtest_filter = "DO_NOT_RUN";
system("rm -f test_ob_log_miner_utils.log");
oceanbase::ObLogger &logger = oceanbase::ObLogger::get_logger();
logger.set_file_name("test_ob_log_miner_utils.log", true, false);
logger.set_log_level("DEBUG");
logger.set_enable_async_log(false);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}