什么是内部表?
内部表(inner_table)是对OBServer内部运行过程一些信息的记录,OB在4.x版本源码(src\share\inner_table\ob_inner_table_schema_def.py)里将内部表分为了以下12类:
# OB use disjoint table_id ranges to define different kinds of tables:
# - (0, 100) : Core Table
# - (0, 10000) : System Table
# - (10000, 15000) : MySQL Virtual Table
# - (15000, 20000) : Oracle Virtual Table
# - (20000, 25000) : MySQL System View
# - (25000, 30000) : Oracle System View
# - (50000, 60000) : Lob meta table
# - (60000, 70000) : Lob piece table
# - (100000, 200000) : System table Index
# - (15305, <20000) : Oracle Real Agent table Index
# - (500000, ~) : User Table
...
################################################################################
# Lob Table (50000, 70000)
################################################################################
# lob table id is correspond to its data_table_id, related schemas will be generated automatically.
-
核心系统表:目前核心系统表OB只定义了4个,序号并不连续,是因为OB对于使用过的ID即使表已经不再使用了,ID也不会重新利用,都为实体表。
例:1: __all_core_table, 3:__all_table, 4:__all_column, 5:__all_ddl_operation -
系统表:
例:108:__all_tenant,116:__all_zone,124:__all_unit_config
记录的都是集群运行所涉及的信息,都为实体表。
-
MySQL虚拟表:虚拟表(virtual table)
-
Oracle虚拟表:虚拟表(virtual table)
-
MySQL系统视图:都为视图(view),虚拟表
-
Oracle系统视图:都为视图(view),虚拟表
虽然OB把内部表分为了很多类,但是实现逻辑总体可以分成三类:一类实体表,一类虚拟表,一类视图。接下来的内容可以说是实现逻辑,也可以说是作为一个开发人员,如何看懂OB代码的逻辑。
内部表的生成流程
内部表由于每次版本更新可能都会有修改,所以目前OB对于内部表是有一套生成代码的脚本的,也就是说只要在相关的Python脚本内把表定义好,运行脚本就会自动生成对应的C++内核代码,上文提到的所有内部表都在脚本中有定义,这套脚本主要是依靠两个文件:
src\share\inner_table\ob_inner_table_schema_def.py ##内部表定义脚本
src\share\inner_table\generate_inner_table_schema.py ##内部表生成脚本
使用逻辑就是在ob_inner_table_schema_def.py文件中把想要添加/修改的内部表的结构写好,然后执行generate_inner_table_schema.py,相关的内核C++代码就会生成。
内部表的具体实现逻辑
首先找到涉及的是哪张内部表/视图
很多SQL实际上都是去内部表获取对应的信息,但是FROM的名称可能并不一定是内部表/视图的名字,这是由于OB在不断迭代的过程中可能会有内部表舍弃不用或者功能发生了改变的情况,如果直接将内部表名称暴露在外,每迭代一次对于外部人员(客户)就会造成使用上的不变,也不方便统一管理,所以OB在4.x版本增加了很多视图,并且期望用户能够使用这些视图去查看信息,接下来以一个系统视图来举例:oceanbase.DBA_*里都是获取整个数据库中的相关信息的视图,此时找一个视图对应的内部表:oceanbase.DBA_OB_LS_REPLICA_TASKS,可以通过explain SQL语句的方式看这条SQL查询的具体是哪个表:
EXPLAIN SELECT * from oceanbase.DBA_OB_LS_REPLICA_TASKS;
=========================================================================
|ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)|
-------------------------------------------------------------------------
|0 |TABLE RANGE SCAN|__ALL_VIRTUAL_LS_REPLICA_TASK|1 |3 |
=========================================================================
这里就可以看出DBA_OB_LS_REPLICA_TASKS实际上查询的是__all_virtual_ls_replica_task这张虚拟表。
还有种情况就是类似SHOW COLUMNS这种直接封装了内部逻辑的情况,这种情况可以通过查询log日志的方式查出来,首先需要打开show命令的trace log,把plan_cache关掉避免直接走缓存,再把日志的记录级别改为debug级,然后就去用show columns查询视图,通过last_trace_id这个方法去获取sql的trace_id,最后去日志中找到结论:
set ob_enable_show_trace = 1; //打开链路追踪
set ob_enable_plan_cache = 0; //关闭计划缓存
set ob_log_level = debug; //将session级的log日志级别设置为debug
show columns from oceanbase.DBA_OB_LS_REPLICA_TASKS;
...
select last_trace_id() from dual;
+-----------------------------------+
| last_trace_id() |
+-----------------------------------+
| YB420A33FC7D-0005FB7AECE9F977-0-0 |
+-----------------------------------+
##去日志中grep这个trace_id //observer/log/
grep YB420A33FC7D-0005FB7AECE9F977-0-0 observer.log* | grep 'generate_normal_tsc'
observer.log.20230706154855073:[2023-07-06 15:44:51.244626] TRACE [SQL.ENG] generate_normal_tsc (ob_static_engine_cg.cpp:3771)
[136677][T1_L0_G0][T1][YB420A33FC7D-0005FB7AECE9F977-0-0] [lt=540] CG index table scan(spec.tsc_ctdef_.scan_ctdef_.ref_table_id_=10002, spec.table_loc_id_=18446744073709551614,
spec.ref_table_id_=10002, op.get_ref_table_id()=10002, op.get_index_table_id()=10002, tbl_name=__tenant_virtual_table_column, index_name=)
generate_normal_tsc是用作定位的,从grep到的trace_id日志中再次grep generate_normal_tsc可以找到tbl_name=__tenant_virtual_table_column即show columns使用的内部表。
然后找到这个内部表的实现过程
首先说一个定义(definition)实现过程最简单的,system view视图或者information schema,之所以说它们最简单是因为它们并不依赖某一张表,而是直接就在结构定义中把查询sql写出来了,就是一个简单的sql查询返回展示结果的过程:
def_table_schema(
owner = 'xiaofeng.lby',
table_name = 'DBA_COL_COMMENTS',
database_id = 'OB_ORA_SYS_DATABASE_ID',
table_id = '25033',
table_type = 'SYSTEM_VIEW',
rowkey_columns = [],
normal_columns = [],
gm_columns = [],
in_tenant_space = True,
view_definition = """
SELECT
A.DATABASE_NAME AS OWNER,
CAST(B.TABLE_NAME AS VARCHAR2(128)) AS TABLE_NAME,
C.COLUMN_NAME AS COLUMN_NAME,
CAST(C."COMMENT" AS VARCHAR(4000)) AS COMMENTS
FROM
SYS.ALL_VIRTUAL_DATABASE_REAL_AGENT A,
SYS.ALL_VIRTUAL_TABLE_REAL_AGENT B,
SYS.ALL_VIRTUAL_COLUMN_REAL_AGENT C
WHERE
A.DATABASE_ID = B.DATABASE_ID
AND B.TABLE_ID = C.TABLE_ID
AND A.TENANT_ID = EFFECTIVE_TENANT_ID()
AND B.TENANT_ID = EFFECTIVE_TENANT_ID()
AND C.TENANT_ID = EFFECTIVE_TENANT_ID()
AND B.TABLE_TYPE != 12 AND B.TABLE_TYPE != 13
""".replace("\n", " ")
)
然后来说明实体表和虚拟表,实体表和虚拟表的实现方式是不同的:
实体表实际上实现分为两部分,第一部分是结构实现(schema),第二部分是数据插入实现。
结构实现站在开发者角度已经通过ob_inner_table_schema_def.py脚本完成了,这里以__all_tenant这个实体系统表举例,相关结构定义如下:
all_tenant_def = dict(
owner = 'yanmu.ztl',
table_name = '__all_tenant',
table_type = 'SYSTEM_TABLE',
table_id = '108',
gm_columns = ['gmt_create', 'gmt_modified'],
rowkey_columns = [
('tenant_id', 'int'),
],
normal_columns = [
('tenant_name', 'varchar:OB_MAX_TENANT_NAME_LENGTH_STORE'),
('zone_list', 'varchar:MAX_ZONE_LIST_LENGTH'),
('primary_zone', 'varchar:MAX_ZONE_LENGTH', 'true'),
('locked', 'int'),
('collation_type', 'int'),
('info', 'varchar:OB_MAX_TENANT_INFO_LENGTH'),
('locality', 'varchar:MAX_LOCALITY_LENGTH', 'false', ''),
('previous_locality', 'varchar:MAX_LOCALITY_LENGTH', 'false', ''),
('default_tablegroup_id', 'int', 'false', 'OB_INVALID_ID'),
('compatibility_mode', 'int', 'false', '0'),
('drop_tenant_time', 'int', 'false', 'OB_INVALID_TIMESTAMP'),
('status', 'varchar:MAX_TENANT_STATUS_LENGTH', 'false', 'NORMAL'),
('in_recyclebin', 'int', 'false', '0'),
('arbitration_service_status', 'varchar:OB_DEFAULT_STATUS_LENTH', 'false', 'DISABLED'),
],
)
def_table_schema(**all_tenant_def)
Python文件运行后生成的就是很多ob_inner_table_schema.*.cpp文件,全局搜索all_tenant_schema可以搜到__all_tenant这个实体表每一个column_name的实现过程,这里以tenant_name这个column举例:
if (OB_SUCC(ret)) {
ADD_COLUMN_SCHEMA("tenant_name", //column_name
++column_id, //column_id
0, //rowkey_id
0, //index_id
0, //part_key_pos
ObVarcharType, //column_type
CS_TYPE_INVALID, //column_collation_type
OB_MAX_TENANT_NAME_LENGTH_STORE, //column_length
-1, //column_precision
-1, //column_scale
false, //is_nullable
false); //is_autoincrement
}
接下来看实体表的数据是如何插入的,这里一般的思路为首先了解在数据库层面一般会在什么时候会往__all_tenant表中插入数据,对于OB而言,__all_tenant是在每次新创建tenant时会有数据变化,所以首先通过全局搜索create_tenant字段来找到突破口,创建租户对于OB是一个ddl行为,所以ob_ddl_service这个名字就会引起注意,cpp文件中涉及create_tenant的代码如下:
else if (OB_FAIL(ddl_operator.create_tenant(tenant_schema, OB_DDL_ADD_TENANT, trans))) {
LOG_WARN("create tenant failed", K(tenant_schema), K(ret));
这两行代码理解起来就是如果OB_FAIL的内容执行失败了,就会打印LOG_WARN,接下来再查看create_tenant引用的是哪里的代码:
int ObDDLOperator::create_tenant(ObTenantSchema &tenant_schema,
const ObSchemaOperationType op,
ObMySQLTransaction &trans,
const ObString *ddl_stmt_str/*=NULL*/)
{
.
.
.
if (OB_FAIL(schema_service->get_tenant_sql_service().insert_tenant(
tenant_schema, op, trans, ddl_stmt_str))) {
LOG_WARN("insert tenant failed", K(tenant_schema), K(ret));
}
.
.
.
}
由于需要的是insert的代码,所以大部分的代码内容并不需要关注,只要从create_tenant中找到真正插入数据的部分就可以了,也就是上面代码中的insert_tenant,然后再看这个insert_tenant是引用自哪里:
int ObTenantSqlService::insert_tenant(
const ObTenantSchema &tenant_schema,
const ObSchemaOperationType op,
ObISQLClient &sql_client,
const ObString *ddl_stmt_str)
{
int ret = OB_SUCCESS;
if (!tenant_schema.is_valid()) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid tenant schema", K(tenant_schema), K(ret));
} else if (OB_FAIL(replace_tenant(tenant_schema, op, sql_client, ddl_stmt_str))) {
LOG_WARN("replace_tenant failed", K(tenant_schema), K(op), K(ret));
}
return ret;
}
这里可以看到实际上OB是用的replace_tenant这个方法去插入数据的,接下来再次查看replace_tenant的引用,就找到了最终的插入数据的实现代码,也是在同一个cpp文件中:
int ObTenantSqlService::replace_tenant(
const ObTenantSchema &tenant_schema,
const ObSchemaOperationType op,
common::ObISQLClient &sql_client,
const ObString *ddl_stmt_str)
{
...
...
...
// insert into __all_tenant
if (OB_SUCC(ret)) {
ObDMLExecHelper exec(sql_client, OB_SYS_TENANT_ID);
if (OB_FAIL(exec.exec_insert_update(OB_ALL_TENANT_TNAME, dml, affected_rows))) {
LOG_WARN("execute insert update failed", KR(ret));
} else if (0 != affected_rows && 1 != affected_rows && 2 != affected_rows) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("affected_rows unexpected", K(affected_rows), K(ret));
} else {
OB_LOG(INFO, "replace tenant success", K(affected_rows), K(tenant_schema));
}
}
...
...
...
}
exec_insert_update是OB封装的用来插入sql的方法,对于__all_tenant这样一张实体表而言,通过插入sql的方式加新数据也是合理的,这种方式阿里也叫inner sql。
在了解虚拟表的实现方式前,需要了解虚拟表在OB实际上是又分为两类的:
一类是直接建立在实体表上的虚拟表
一类是自己重新定义的虚拟表
二者区别是可以从定义结构中看出来:
def_table_schema(**gen_iterate_virtual_table_def(
table_id = '12362',
table_name = '__all_virtual_core_table',
keywords = all_def_keywords['__all_core_table']))
def_table_schema(
owner = 'tushicheng.tsc',
table_name = '__all_virtual_malloc_sample_info',
table_id = '12363',
table_type = 'VIRTUAL_TABLE',
gm_columns = [],
in_tenant_space = True,
rowkey_columns = [],
normal_columns = [
('svr_ip', 'varchar:MAX_IP_ADDR_LENGTH'),
('svr_port', 'int'),
('tenant_id', 'int'),
('ctx_id', 'int'),
('mod_name', 'varchar:OB_MAX_CHAR_LENGTH'),
('back_trace', 'varchar:DEFAULT_BUF_LENGTH'),
('ctx_name', 'varchar:OB_MAX_CHAR_LENGTH'),
('alloc_count', 'int'),
('alloc_bytes', 'int'),
],
vtable_route_policy = 'distributed',
partition_columns = ['svr_ip', 'svr_port'],
)
直接建立在实体表上的虚拟表就是上面代码中的__all_virtual_core_table,它是直接在实体表__all_core_table上建立的,没有自定义的columns,梳理逻辑和实体表类似,在全局搜索virtual_core_table_shcema就可以搜到通过脚本生成的实际架构代码,可以看出在架构生成方面和实体表是完全一致的:
int ObInnerTableSchema::all_virtual_core_table_schema(ObTableSchema &table_schema)
{
...
if (OB_SUCC(ret)) {
ADD_COLUMN_SCHEMA("tenant_id", //column_name
++column_id, //column_id
1, //rowkey_id
0, //index_id
0, //part_key_pos
ObIntType, //column_type
CS_TYPE_INVALID, //column_collation_type
sizeof(int64_t), //column_length
-1, //column_precision
-1, //column_scale
false, //is_nullable
false); //is_autoincrement
}
...
在插入数据方面,这种类型的表主要依靠一种迭代器,且相关代码都在ob_inner_table_schema_misc.cpp这个文件中:
case OB_ALL_VIRTUAL_CORE_TABLE_TID: {
ObIterateVirtualTable *iter = NULL;
if (OB_FAIL(NEW_VIRTUAL_TABLE(ObIterateVirtualTable, iter))) {
SERVER_LOG(WARN, "create virtual table iterator failed", K(ret));
} else if (OB_FAIL(iter->init(OB_ALL_CORE_TABLE_TID, index_schema, params))) {
SERVER_LOG(WARN, "virtual table iter init failed", K(ret));
iter->~ObIterateVirtualTable();
allocator.free(iter);
iter = NULL;
} else {
vt_iter = iter;
}
break;
}
之所以有这种类型的虚拟表是因为OB不想让客户能直接看到这类核心的实体表。
自己重新定义的虚拟表实现方式和其他表有所不同,以上面代码中的__all_virtual_malloc_sample_info为例,结构实现和物理表是一样的,但是插入数据的实现就不同了,首先第一步查询步骤和实体表类似,全局搜索下这个名称,在完全不知道这个虚拟表的作用时,可以在末尾加上_TID,即搜索的是__all_virtual_malloc_sample_info_tid,这是因为OB这么庞大的代码群为了效率不可能通过查找实际的英文名运行,绝大部分的内部表都有一个TID,通过搜索可以查到:
case OB_ALL_VIRTUAL_MALLOC_SAMPLE_INFO_TID: {
ObMallocSampleInfo *malloc_sample_info = NULL;
if (OB_SUCC(NEW_VIRTUAL_TABLE(ObMallocSampleInfo, malloc_sample_info))) {
malloc_sample_info->set_allocator(&allocator);
vt_iter = static_cast<ObVirtualTableIterator *>(malloc_sample_info);
}
break;
这里可以发现这个iterator_factory.cpp文件直接引用了ObMallocSampleInfo这个类,直接通过这个类来查看虚拟表数据部分的具体实现,通过查看多个虚拟表,推测虚拟表的内容填充都是通过inner_get_next_row这个名字的函数完成的,只不过完成的方式上有所不同,ObMallocSampleInfo完成的方式如下:
int ObMallocSampleInfo::inner_get_next_row(ObNewRow *&row)
{
int ret = OB_SUCCESS;
if (!opened_) {
col_count_ = output_column_ids_.count();
if (OB_FAIL(malloc_sample_map_.create(1000, "MallocInfoMap", "MallocInfoMap"))) {
SERVER_LOG(WARN, "create memory info map failed", K(ret));
} else {
ret = ObMemoryDump::get_instance().load_malloc_sample_map(malloc_sample_map_);
if (OB_SUCC(ret)) {
it_ = malloc_sample_map_.begin();
opened_ = true;
}
}
}
for (; OB_SUCC(ret) && it_ != malloc_sample_map_.end(); ++it_) {
if (is_sys_tenant(effective_tenant_id_) || effective_tenant_id_ == it_->first.tenant_id_) {
if (OB_FAIL(fill_row(row))) {
SERVER_LOG(WARN, "failed to fill row", K(ret));
}
break;
}
}
.........
从代码中可以看到,真正插入数据是通过一个循环执行fill_row函数完成的,比较简单粗暴,就是直接给每个cell插入数值:
int ObMallocSampleInfo::fill_row(ObNewRow *&row)
{
int ret = OB_SUCCESS;
ObObj *cells = NULL;
if (OB_ISNULL(cells = cur_row_.cells_)) {
ret = OB_ERR_UNEXPECTED;
SERVER_LOG(WARN, "cur row cell is NULL", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < col_count_; ++i) {
const uint64_t col_id = output_column_ids_.at(i);
switch (col_id) {
case SVR_IP: {
cells[i].set_varchar(ip_buf_);
cells[i].set_collation_type(
ObCharset::get_default_collation(ObCharset::get_default_charset()));
break;
}
case SVR_PORT: {
cells[i].set_int(GCONF.self_addr_.get_port());
break;
}
case TENANT_ID: {
cells[i].set_int(it_->first.tenant_id_);
break;
}
case CTX_ID: {
cells[i].set_int(it_->first.ctx_id_);
break;
}
为了更好的了解虚拟表的扫描插入数据方法,换一个虚拟表看看,__tenant_virtual_show_create_database表:
def_table_schema(
owner = 'xiaofeng.lby',
table_name = '__tenant_virtual_show_create_database',
table_id = '10004',
table_type = 'VIRTUAL_TABLE',
gm_columns = [],
rowkey_columns = [
('database_id', 'int'),
],
in_tenant_space = True,
normal_columns = [
('database_name', 'varchar:OB_MAX_DATABASE_NAME_LENGTH'),
('create_database', 'varchar:DATABASE_DEFINE_LENGTH'),
('create_database_with_if_not_exists', 'varchar:DATABASE_DEFINE_LENGTH'),
],
)
通过上问提到的类似的查询方法,可以查到在__tenant_virtual_show_create_database的inner_get_next_row函数里,用到了scanner_it_这种迭代方法,并不是循环:
int ObShowCreateDatabase::inner_get_next_row(common::ObNewRow *&row)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(allocator_) || OB_ISNULL(schema_guard_)) {
ret = OB_NOT_INIT;
LOG_WARN("data member is NULL", K(ret), K(allocator_), K(schema_guard_));
} else {
if (!start_to_read_) {
const ObDatabaseSchema *db_schema = NULL;
uint64_t show_database_id = OB_INVALID_ID;
if (OB_FAIL(calc_show_database_id(show_database_id))) {
LOG_WARN("fail to calc show database id", K(ret));
} else if (OB_UNLIKELY(OB_INVALID_ID == show_database_id)) {
ret = OB_ERR_UNEXPECTED;
LOG_USER_ERROR(OB_ERR_UNEXPECTED, "this table is used for show clause, can't be selected");
} else if (OB_FAIL(schema_guard_->get_database_schema(effective_tenant_id_,
show_database_id, db_schema))) {
LOG_WARN("failed to get database_schema", K(ret), K_(effective_tenant_id), K(show_database_id));
} else if (OB_UNLIKELY(NULL == db_schema)) {
ret = OB_ERR_BAD_DATABASE;
LOG_WARN("db_schema is null", K(ret), K(show_database_id));
} else {
if (OB_FAIL(fill_row_cells(show_database_id, db_schema->get_database_name_str()))) {
LOG_WARN("fail to fill row cells", K(ret),
K(show_database_id), K(db_schema->get_database_name_str()));
} else if (OB_FAIL(scanner_.add_row(cur_row_))) {
LOG_WARN("fail to add row", K(ret), K(cur_row_));
} else {
scanner_it_ = scanner_.begin();
start_to_read_ = true;
}
}
}
if (OB_LIKELY(OB_SUCCESS == ret && start_to_read_)) {
if (OB_FAIL(scanner_it_.get_next_row(cur_row_))) {
if (OB_UNLIKELY(OB_ITER_END != ret)) {
LOG_WARN("fail to get next row", K(ret));
}
} else {
row = &cur_row_;
}
}
}
return ret;
}
工作原理大致就是先get_database_schema,然后通过fill_row_cells把内容填充到这一行的cell里,再通过add_row新增行,最后通过get_next_row压缩。fill_row_cells的实现其实和上一个虚拟表是类似的,也是直接给cell插入数据,多看几个虚拟表可以发现真正插数这个函数的名字都大差不差,fill_cells,fill_rows,fill_row_cells等等这样:
int ObShowCreateDatabase::fill_row_cells(uint64_t show_database_id,
const ObString &database_name)
{
switch(col_id) {
case OB_APP_MIN_COLUMN_ID: {
// database_id
cur_row_.cells_[cell_idx].set_int(show_database_id);
break;
}
case OB_APP_MIN_COLUMN_ID + 1: {
// database_name
cur_row_.cells_[cell_idx].set_varchar(database_name);
cur_row_.cells_[cell_idx].set_collation_type(
ObCharset::get_default_collation(ObCharset::get_default_charset()));
break;
}
case OB_APP_MIN_COLUMN_ID + 2: {
// create_database
ObSchemaPrinter schema_printer(*schema_guard_, strict_mode);
int64_t pos = 0;
if (OB_FAIL(schema_printer.print_database_definiton(effective_tenant_id_,
show_database_id,
false,
db_def_buf,
db_def_buf_size,
pos))) {
LOG_WARN("Generate database definition failed",
K(ret), K(effective_tenant_id_), K(show_database_id));
} else {
ObString value_str(static_cast<int32_t>(db_def_buf_size),
static_cast<int32_t>(pos), db_def_buf);
cur_row_.cells_[cell_idx].set_varchar(value_str);
cur_row_.cells_[cell_idx].set_collation_type(
ObCharset::get_default_collation(ObCharset::get_default_charset()));
}
break;
}
还有一种情况比较特殊,和虚拟表是属于observer还是rootserver有关,和observer有关的虚拟表都是上述的方法,而rootserver的虚拟表则是下面这种方式,直接取到的core_table里的column_schema后加到Columns里:
int ObVritualCoreInnerTable::get_full_row(
const uint64_t tenant_id,
const ObTableSchema *table,
const ObCoreTableProxy &core_table,
ObIArray<Column> &columns)
{
int ret = OB_SUCCESS;
if (!inited_) {
ret = OB_NOT_INIT;
LOG_WARN("not init", K(ret));
} else if (NULL == table) {
// core_table doesn't need to check
ret = OB_INVALID_ARGUMENT;
LOG_WARN("table is null", K(ret));
} else {
const ObColumnSchemaV2 *column_schema = NULL;
const char *column_name = NULL;
ObString str_column;
for (int64_t i = 0; OB_SUCC(ret) && i < output_column_ids_.count(); ++i) {
if (NULL == (column_schema = table->get_column_schema(output_column_ids_.at(i)))) {
ret = OB_SCHEMA_ERROR;
LOG_WARN("column id not exist", "column_id", output_column_ids_.at(i), K(ret));
} else if (NULL == (column_name = column_schema->get_column_name())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("column name is null", K(column_schema), K(ret));
} else {
int inner_ret = OB_SUCCESS;
if (ObVarcharType == column_schema->get_data_type()
|| ObLongTextType == column_schema->get_data_type()) {
str_column.reset();
if (OB_SUCCESS == (inner_ret = core_table.get_varchar(
column_name, str_column))) {
if (ObVarcharType == column_schema->get_data_type()) {
ADD_COLUMN(set_varchar, table, column_name, str_column, columns);
} else if (ObLongTextType == column_schema->get_data_type()) {
ADD_TEXT_COLUMN(ObLongTextType, table, column_name, str_column, columns);
}
} else if (OB_ERR_NULL_VALUE == inner_ret) {
ADD_NULL_COLUMN(table, column_name, columns);
} else {
ret = inner_ret;
LOG_WARN("get_varchar failed", K(column_name), K(ret));
}
} else if (ObIntType == column_schema->get_data_type()
|| ObTinyIntType == column_schema->get_data_type()
|| ObUInt64Type == column_schema->get_data_type()) {
int64_t int_column = 0;
if (OB_SUCCESS == (inner_ret = core_table.get_int(
column_name, int_column))) {
if (0 == column_schema->get_column_name_str().case_compare("tenant_id")) {
int_column = tenant_id;
}
if (ObIntType == column_schema->get_data_type()) {
ADD_COLUMN(set_int, table, column_name, int_column, columns);
} else if (ObTinyIntType == column_schema->get_data_type()) {
ADD_COLUMN(set_tinyint, table, column_name, static_cast<int8_t>(int_column), columns);
} else {
ADD_COLUMN(set_uint64, table, column_name, int_column, columns);
}
} else if (OB_ERR_NULL_VALUE == inner_ret) {
ADD_NULL_COLUMN(table, column_name, columns);
} else {
ret = inner_ret;
LOG_WARN("get_int failed", K(column_name), K(ret));
}
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("data type is not expected", "data_type",
column_schema->get_data_type(), K(ret));
}
}
}
}
return ret;
}