Co-authored-by: tino247 <tino247@126.com> Co-authored-by: BinChenn <binchenn.bc@gmail.com> Co-authored-by: HaHaJeff <jeffzhouhhh@gmail.com>
346 lines
12 KiB
C++
346 lines
12 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#define USING_LOG_PREFIX SERVER
|
|
|
|
#include "ob_agent_virtual_table.h"
|
|
#include "share/ob_i_tablet_scan.h"
|
|
#include "share/schema/ob_schema_struct.h"
|
|
#include "observer/ob_server_struct.h"
|
|
#include "observer/ob_inner_sql_result.h"
|
|
#include "lib/string/ob_sql_string.h"
|
|
#include "observer/ob_server_struct.h"
|
|
|
|
namespace oceanbase
|
|
{
|
|
|
|
using namespace common;
|
|
using namespace share;
|
|
|
|
namespace observer
|
|
{
|
|
|
|
// convert mysql type to oracle type, only types used in inter table schema define are supported.
|
|
// see replace_agent_table_columns_def of generate_inner_table_schema.py.
|
|
static int int2number(const ObObj &src, ObObj &dst, ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber num;
|
|
if (OB_FAIL(num.from(src.get_int(), allocator))) {
|
|
LOG_WARN("convert int to number failed", K(ret), K(src));
|
|
} else {
|
|
dst.set_number(num);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int uint2number(const ObObj &src, ObObj &dst, ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
number::ObNumber num;
|
|
if (OB_FAIL(num.from(src.get_uint64(), allocator))) {
|
|
LOG_WARN("convert int to number failed", K(ret), K(src));
|
|
} else {
|
|
dst.set_number(num);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int double2number(const ObObj &src, ObObj &dst, ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
// copy from ob_obj_cast.cpp:
|
|
static const int64_t MAX_DOUBLE_PRINT_SIZE = 64;
|
|
char buf[MAX_DOUBLE_PRINT_SIZE];
|
|
MEMSET(buf, 0, MAX_DOUBLE_PRINT_SIZE);
|
|
snprintf(buf, MAX_DOUBLE_PRINT_SIZE, "%lf", src.get_double());
|
|
number::ObNumber nmb;
|
|
if (OB_FAIL(nmb.from(buf, allocator))) {
|
|
LOG_WARN("convert double to number failed", K(ret), K(src));
|
|
} else {
|
|
dst.set_number(nmb);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int varchar2varchar(const ObObj &src, ObObj &dst, ObIAllocator &)
|
|
{
|
|
if (src.val_len_ <= 0) {
|
|
dst.set_null();
|
|
} else {
|
|
dst = src;
|
|
//对于oracle租户内部表,从表、列到字符串类型确保所有collation为CS_TYPE_UTF8MB4_BIN
|
|
dst.set_collation_type(ObCollationType::CS_TYPE_UTF8MB4_BIN);
|
|
}
|
|
return OB_SUCCESS;
|
|
}
|
|
|
|
static int number2number(const ObObj &src, ObObj &dst, ObIAllocator &)
|
|
{
|
|
dst = src;
|
|
return OB_SUCCESS;
|
|
}
|
|
|
|
static int longtext2longtext(const ObObj &src, ObObj &dst, ObIAllocator &)
|
|
{
|
|
dst = src;
|
|
return OB_SUCCESS;
|
|
}
|
|
|
|
static int timestamp2otimestamp(const ObObj &src, ObObj &dst, ObIAllocator &)
|
|
{
|
|
ObOTimestampData td;
|
|
td.time_us_ = src.get_timestamp();
|
|
td.time_ctx_.tail_nsec_ = 0;
|
|
dst.set_otimestamp_value(ObTimestampLTZType, td);
|
|
return OB_SUCCESS;
|
|
}
|
|
|
|
ObAgentVirtualTable::ObAgentVirtualTable() : general_tenant_id_(OB_INVALID_TENANT_ID),
|
|
mode_(lib::Worker::CompatMode::ORACLE)
|
|
{
|
|
}
|
|
|
|
ObAgentVirtualTable::~ObAgentVirtualTable()
|
|
{
|
|
}
|
|
|
|
// if base table is in tenant space, sys_tenant_base_table = true.
|
|
// if virtual table is sys agent table, only_sys_data = true
|
|
int ObAgentVirtualTable::init(
|
|
const uint64_t pure_table_id,
|
|
const bool sys_tenant_base_table,
|
|
const share::schema::ObTableSchema *index_table,
|
|
const ObVTableScanParam &scan_param,
|
|
const bool only_sys_data,
|
|
const lib::Worker::CompatMode &mode)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_INVALID_ID == pure_table_id
|
|
|| OB_ISNULL(index_table)
|
|
|| !scan_param.ObVTableScanParam::is_valid()) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid argument", K(ret), K(pure_table_id), KP(index_table), K(scan_param));
|
|
} else {
|
|
only_sys_data_ = only_sys_data;
|
|
mode_ = mode;
|
|
const uint64_t base_tenant_id = (sys_tenant_base_table || only_sys_data) ?
|
|
OB_SYS_TENANT_ID : scan_param.tenant_id_;
|
|
if (OB_FAIL(ObAgentTableBase::init(base_tenant_id, pure_table_id, index_table, scan_param))) {
|
|
LOG_WARN("init base agent table failed", K(ret));
|
|
} else if (OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_TID == pure_table_id) {
|
|
general_tenant_id_ = scan_param.tenant_id_;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObAgentVirtualTable::do_open()
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSqlString sql;
|
|
if (OB_FAIL(ObAgentTableBase::do_open())) {
|
|
LOG_WARN("base agent table open failed", KR(ret));
|
|
} else if (OB_FAIL(construct_sql(base_tenant_id_, sql))) {
|
|
LOG_WARN("construct sql failed", KR(ret), K(base_tenant_id_));
|
|
} else if (OB_FAIL(GCTX.sql_proxy_->read(*sql_res_, base_tenant_id_, sql.ptr()))) {
|
|
LOG_WARN("execute sql failed", KR(ret), K(base_tenant_id_), K(sql));
|
|
} else if (OB_ISNULL(sql_res_->get_result())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("NULL sql executing result", KR(ret), K(base_tenant_id_), K(sql));
|
|
} else {
|
|
inner_sql_res_ = static_cast<ObInnerSQLResult *>(sql_res_->get_result());
|
|
if (general_tenant_id_ != OB_INVALID_TENANT_ID) {
|
|
inner_sql_res_->result_set().get_session().switch_tenant(general_tenant_id_);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObAgentVirtualTable::set_convert_func(convert_func_t &func,
|
|
const schema::ObColumnSchemaV2 &col, const schema::ObColumnSchemaV2 &base_col)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (lib::Worker::CompatMode::MYSQL == mode_) {
|
|
// do not set_convert_func
|
|
} else {
|
|
ObObjType from = base_col.get_data_type();
|
|
ObObjType to = col.get_data_type();
|
|
ObObjTypeClass fromclass = ob_obj_type_class(from);
|
|
if (ObVarcharType == from && ObVarcharType == to) {
|
|
func = varchar2varchar;
|
|
} else if (ObNumberType == to && ObNumberType == from) {
|
|
func = number2number;
|
|
} else if (ObNumberType == to && ObIntTC == fromclass) {
|
|
func = int2number;
|
|
} else if (ObNumberType == to && ObUIntTC == fromclass) {
|
|
func = uint2number;
|
|
} else if (ObNumberType == to && ObDoubleTC == fromclass) {
|
|
func = double2number;
|
|
} else if (ObTimestampType == from && ObTimestampLTZType == to) {
|
|
func = timestamp2otimestamp;
|
|
} else if (ObLongTextType == from && ObLongTextType == to) {
|
|
func = longtext2longtext;
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unsupported type convert",
|
|
K(ret), K(from), K(to), K(col), K(base_col));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObAgentVirtualTable::init_non_exist_map_item(
|
|
MapItem &, const schema::ObColumnSchemaV2 &col)
|
|
{
|
|
int ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("base table's column not found", K(ret), K(col), K(*base_table_));
|
|
return ret;
|
|
}
|
|
|
|
int ObAgentVirtualTable::add_extra_condition(common::ObSqlString &sql)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
lib::CompatModeGuard g(lib::Worker::CompatMode::MYSQL);
|
|
uint64_t tenant_id = OB_SYS_TENANT_ID;
|
|
if (NULL == table_schema_) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table schema is NULL", K(ret));
|
|
} else {
|
|
if (!only_sys_data_) {
|
|
tenant_id = table_schema_->get_tenant_id();
|
|
}
|
|
}
|
|
bool need_tenant_id = true;
|
|
if (OB_FAIL(ret)) {
|
|
} else if (lib::Worker::CompatMode::MYSQL == mode_ && is_sys_tenant(effective_tenant_id_)) {
|
|
LOG_TRACE("do not add tenant_id filter", K_(mode), K_(effective_tenant_id));
|
|
} else if (OB_FAIL(should_add_tenant_condition(need_tenant_id, tenant_id))) {
|
|
LOG_WARN("failed to get is_add_tenant_condition flag", K(ret));
|
|
} else if (need_tenant_id && NULL != table_schema_->get_column_schema("tenant_id")) {
|
|
if (OB_FAIL(sql.append_fmt(" AND tenant_id = %ld", tenant_id))) {
|
|
LOG_WARN("append sql failed", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObAgentVirtualTable::inner_get_next_row(common::ObNewRow *&row)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const ObNewRow *base_row = NULL;
|
|
if (!opened_ && OB_FAIL(do_open())) {
|
|
LOG_WARN("do open failed", K(ret));
|
|
} else if (OB_ISNULL(inner_sql_res_) || OB_ISNULL(scan_param_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("null scan parameter or result", K(ret));
|
|
} else if (OB_FAIL(inner_sql_res_->next())) {
|
|
if (OB_ITER_END != ret) {
|
|
LOG_WARN("iterate inner sql result failed", K(ret));
|
|
}
|
|
} else if (OB_ISNULL(base_row = inner_sql_res_->get_row())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("NULL row", K(ret));
|
|
} else if (OB_ISNULL(cur_row_.cells_) || cur_row_.count_ < scan_param_->column_ids_.count()
|
|
|| base_row->get_count() < scan_param_->column_ids_.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("count mismatch", K(ret), KP(cur_row_.cells_), K(cur_row_.count_),
|
|
K(base_row->get_count()), K(scan_param_->column_ids_.count()));
|
|
} else {
|
|
convert_alloc_.reuse();
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < scan_param_->column_ids_.count(); i++) {
|
|
const ObObj &input = base_row->get_cell(i);
|
|
if (input.is_null()) {
|
|
cur_row_.cells_[i].set_null();
|
|
} else if (lib::Worker::CompatMode::MYSQL == mode_) {
|
|
cur_row_.cells_[i] = input;
|
|
LOG_INFO("mysql compat mode agent do not do convert", KR(ret), K(input));
|
|
} else if (OB_FAIL(mapping_[scan_param_->column_ids_.at(i)].convert_func_(
|
|
input, cur_row_.cells_[i], convert_alloc_))) {
|
|
LOG_WARN("convert obj failed", K(ret), K(input),
|
|
K(mapping_[scan_param_->column_ids_.at(i)]));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
cur_row_.count_ = scan_param_->column_ids_.count();
|
|
row = &cur_row_;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// 如果query range为 (tenant_id, cond1, cond2, ...) <= (v1, v2, v3, ...) and (tenant_id, cond1, cond2, ...) >= (v1', v2', v3', ...)
|
|
// 那么如果在最后的条件加上 and tenant_id = v,也就是
|
|
// (tenant_id, cond1, cond2, ...) <= (v1, v2, v3, ...) and (tenant_id, cond1, cond2, ...) >= (v1', v2', v3', ...) and tenant_id = v
|
|
// 优化器这时候抽不出来query range,只能得到 (tenant_id, min, min), (tenant_id, max, max),
|
|
// 对于某些只支持get操作的表(比如plan_cache_plan_explain),结果集总为空,所以需要加上这个判断条件
|
|
int ObAgentVirtualTable::should_add_tenant_condition(bool &need, const uint64_t tenant_id) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
need = true;
|
|
|
|
if (OB_ISNULL(scan_param_) || OB_ISNULL(index_table_)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid argument", K(scan_param_), K(index_table_), K(ret));
|
|
} else {
|
|
const ObRangeArray &ranges = scan_param_->key_ranges_;
|
|
const ObRowkeyInfo &info = index_table_->get_rowkey_info();
|
|
if (info.get_size() <= 0) {
|
|
LOG_DEBUG("rowkeys is empty", K(ret), K(info.get_size()));
|
|
} else {
|
|
FOREACH_CNT_X(r, ranges, OB_SUCC(ret)) {
|
|
if (r->table_id_ != index_table_->get_table_id()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected index table id", K(*r), K(*index_table_));
|
|
} else if (!r->start_key_.is_valid() || !r->end_key_.is_valid()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("rowkey is invalid", K(ret));
|
|
} else {
|
|
uint64_t cid = 0;
|
|
if (OB_FAIL(info.get_column_id(base_rowkey_offset_, cid))) {
|
|
LOG_WARN("failed to get column id", K(ret), K(base_rowkey_offset_), K(info));
|
|
} else if (0 == mapping_.at(cid).base_col_name_.compare("`tenant_id`")) {
|
|
// not need: rowkey start with tenant_id and all ranges' tenant_id equal to %tenant_id
|
|
const int64_t idx = base_rowkey_offset_;
|
|
const ObRowkey &s = r->start_key_;
|
|
const ObRowkey &e = r->end_key_;
|
|
if (s.get_obj_cnt() > idx
|
|
&& e.get_obj_cnt() > idx
|
|
&& s.get_obj_ptr()[idx] == e.get_obj_ptr()[idx]
|
|
&& s.get_obj_ptr()[idx].is_number()) {
|
|
number::ObNumber nmb;
|
|
s.get_obj_ptr()[idx].get_number(nmb);
|
|
if (nmb.compare(tenant_id) == 0) {
|
|
need = false;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
need = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
} // end namespace observer
|
|
} // end namespace oceanbase
|
|
|