262 lines
11 KiB
C++
262 lines
11 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 SQL_RESV
|
|
#include "sql/resolver/dml/ob_view_table_resolver.h"
|
|
#include "share/schema/ob_table_schema.h"
|
|
#include "common/sql_mode/ob_sql_mode_utils.h"
|
|
#include "sql/parser/ob_parser.h"
|
|
#include "sql/session/ob_sql_session_info.h"
|
|
#include "sql/ob_sql_utils.h"
|
|
namespace oceanbase
|
|
{
|
|
using namespace common;
|
|
using namespace share::schema;
|
|
namespace sql
|
|
{
|
|
int ObViewTableResolver::do_resolve_set_query(const ParseNode &parse_tree,
|
|
ObSelectStmt *&child_stmt,
|
|
const bool is_left_child) /*default false*/
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
child_stmt = NULL;
|
|
ObViewTableResolver child_resolver(params_, view_db_name_, view_name_);
|
|
|
|
child_resolver.set_current_level(current_level_);
|
|
child_resolver.set_current_view_level(current_view_level_);
|
|
child_resolver.set_parent_namespace_resolver(parent_namespace_resolver_);
|
|
child_resolver.set_current_view_item(current_view_item);
|
|
child_resolver.set_parent_view_resolver(parent_view_resolver_);
|
|
child_resolver.set_calc_found_rows(is_left_child && has_calc_found_rows_);
|
|
|
|
if (OB_FAIL(add_cte_table_to_children(child_resolver))) {
|
|
LOG_WARN("failed to add cte table to children", K(ret));
|
|
} else if (OB_FAIL(child_resolver.resolve_child_stmt(parse_tree))) {
|
|
LOG_WARN("failed to resolve child stmt", K(ret));
|
|
} else if (OB_ISNULL(child_stmt = child_resolver.get_child_stmt())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get null child stmt", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObViewTableResolver::expand_view(TableItem &view_item)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(session_info_)) {
|
|
ret = OB_NOT_INIT;
|
|
LOG_WARN("session info is null");
|
|
} else if (OB_FAIL(check_view_circular_reference(view_item))) {
|
|
LOG_WARN("check view circular reference failed", K(ret));
|
|
} else {
|
|
// expand view as subquery which use view name as alias
|
|
const ObTableSchema *view_schema = NULL;
|
|
|
|
if (OB_FAIL(schema_checker_->get_table_schema(session_info_->get_effective_tenant_id(), view_item.ref_id_, view_schema))) {
|
|
LOG_WARN("get table schema failed", K(view_item));
|
|
} else {
|
|
ObViewTableResolver view_resolver(params_, view_db_name_, view_name_);
|
|
view_resolver.set_current_level(current_level_);
|
|
view_resolver.set_current_view_level(current_view_level_ + 1);
|
|
view_resolver.set_view_ref_id(view_item.ref_id_);
|
|
view_resolver.set_current_view_item(view_item);
|
|
view_resolver.set_parent_view_resolver(this);
|
|
view_resolver.set_parent_namespace_resolver(parent_namespace_resolver_);
|
|
if (OB_FAIL(do_expand_view(view_item, view_resolver))) {
|
|
LOG_WARN("do expand view failed", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObViewTableResolver::check_view_circular_reference(const TableItem &view_item)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
// 检查逻辑 :不断往上走,逐层判断view_item是否相同(db_name && tbl_name)
|
|
// -- is_view_stmt() : 判断当前stmt是不是view展开的
|
|
// -- get_view_item() : 展开成当前stmt的view (TableItem)
|
|
// -- get_view_upper_scope_stmt() : view子查询的uppe_scope_stmt
|
|
// 如果存在相互引用,mysql的行为是对最上层的view报错, 因此
|
|
// exist_circular_reference = true 时也会继续循环, 比如:
|
|
// v3引用v2,而v1<->v2相互引用,select * from v3 结果是对v3报错
|
|
ObViewTableResolver *cur_resolver = this;
|
|
if (OB_UNLIKELY(! view_db_name_.empty() && ! view_name_.empty()
|
|
&& 0 == view_db_name_.compare(view_item.database_name_)
|
|
&& 0 == view_name_.compare(view_item.table_name_))) {
|
|
if (is_oracle_mode()) {
|
|
ret = OB_ERR_VIEW_RECURSIVE;
|
|
} else {
|
|
ret = OB_ERR_VIEW_RECURSIVE;
|
|
LOG_USER_ERROR(OB_ERR_VIEW_RECURSIVE, view_db_name_.length(), view_db_name_.ptr(),
|
|
view_name_.length(), view_name_.ptr());
|
|
}
|
|
} else {
|
|
// 原来的检测逻辑存在一个问题,对于开头的这个例子,不应该在创建v3时报错,或者说v1和v2相互引用的情况就不应该出现
|
|
// 而是应该在create or replace v1/v2导致v1和v2相互引用时报错。
|
|
// 虽然现在加了前面这个检测逻辑,在创建视图v时检查定义展开后没有出现v可以避免出现相互引用,
|
|
// 但是原来的检测逻辑也要保留。如果升级前创建了存在循环的视图,升级后select from这个视图,在下面报错。
|
|
do {
|
|
if (OB_UNLIKELY(view_item.ref_id_ == cur_resolver->current_view_item.ref_id_)) {
|
|
ret = OB_ERR_VIEW_RECURSIVE;
|
|
const ObString &db_name = cur_resolver->current_view_item.database_name_;
|
|
const ObString &tbl_name = cur_resolver->current_view_item.table_name_;
|
|
LOG_USER_ERROR(OB_ERR_VIEW_RECURSIVE, db_name.length(), db_name.ptr(),
|
|
tbl_name.length(), tbl_name.ptr());
|
|
} else {
|
|
cur_resolver = cur_resolver->parent_view_resolver_;
|
|
}
|
|
} while(OB_SUCC(ret) && cur_resolver != NULL);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObViewTableResolver::resolve_generate_table(const ParseNode &table_node, const ObString &alias_name, TableItem *&table_item)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObViewTableResolver view_table_resolver(params_, view_db_name_, view_name_);
|
|
//from子查询和当前查询属于平级,因此current level和当前保持一致
|
|
view_table_resolver.set_current_level(current_level_);
|
|
view_table_resolver.set_current_view_level(current_view_level_);
|
|
view_table_resolver.set_parent_namespace_resolver(parent_namespace_resolver_);
|
|
view_table_resolver.set_parent_view_resolver(parent_view_resolver_);
|
|
view_table_resolver.set_current_view_item(current_view_item);
|
|
if (OB_FAIL(view_table_resolver.set_cte_ctx(cte_ctx_, true, true))) {
|
|
LOG_WARN("set cte ctx to child resolver failed", K(ret));
|
|
} else if (OB_FAIL(add_cte_table_to_children(view_table_resolver))) {
|
|
LOG_WARN("add CTE table to children failed", K(ret));
|
|
} else if (OB_FAIL(do_resolve_generate_table(table_node, alias_name, view_table_resolver, table_item))) {
|
|
LOG_WARN("do resolve generate table failed", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// use_sys_tenant 标记是否需要以系统租户的身份获取schema
|
|
int ObViewTableResolver::check_need_use_sys_tenant(bool &use_sys_tenant) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
// 若当前已经是系统租户, 则忽略
|
|
const ObTableSchema *table_schema = NULL;
|
|
if (OB_ISNULL(session_info_)) {
|
|
ret = OB_NOT_INIT;
|
|
LOG_WARN("session info is null");
|
|
} else if (OB_SYS_TENANT_ID == session_info_->get_effective_tenant_id()) {
|
|
use_sys_tenant = false;
|
|
} else {
|
|
use_sys_tenant = true;
|
|
}
|
|
|
|
// 若当前stmt不是系统视图展开的, 则忽略
|
|
if (OB_SUCC(ret) && use_sys_tenant) {
|
|
if (OB_FAIL(schema_checker_->get_table_schema(session_info_->get_effective_tenant_id(), current_view_item.ref_id_, table_schema))) {
|
|
LOG_WARN("fail to get table_schema", K(ret));
|
|
} else if (NULL == table_schema) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("table_schema should not be NULL", K(ret));
|
|
} else if (!table_schema->is_sys_view()) {
|
|
use_sys_tenant = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObViewTableResolver::check_in_sysview(bool &in_sysview) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
in_sysview = is_sys_view(current_view_item.ref_id_);
|
|
return ret;
|
|
}
|
|
|
|
// construct select item from select_expr
|
|
int ObViewTableResolver::set_select_item(SelectItem &select_item, bool is_auto_gen)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObCollationType cs_type = CS_TYPE_INVALID;
|
|
ObSelectStmt *select_stmt = get_select_stmt();
|
|
|
|
if (OB_ISNULL(select_stmt) || OB_ISNULL(session_info_) || OB_ISNULL(select_item.expr_)) {
|
|
ret = OB_NOT_INIT;
|
|
LOG_WARN("select stmt is null", K_(session_info), K(select_stmt), K_(select_item.expr));
|
|
} else if (is_create_view_ && !select_item.is_real_alias_) {
|
|
if (OB_FAIL(ObSelectResolver::set_select_item(select_item, is_auto_gen))) {
|
|
LOG_WARN("set select item failed", K(ret));
|
|
}
|
|
} else if (OB_FAIL(session_info_->get_collation_connection(cs_type))) {
|
|
LOG_WARN("fail to get collation_connection", K(ret));
|
|
} else {
|
|
// 如果子查询列没有别名,超过 64 的话系统则自动为其会生成一个列别名
|
|
if (!is_create_view_ && !select_item.is_real_alias_ && is_auto_gen
|
|
&& select_item.alias_name_.length() > static_cast<size_t>(
|
|
OB_MAX_VIEW_COLUMN_NAME_LENGTH_MYSQL)) {
|
|
ObString tmp_col_name;
|
|
ObString col_name;
|
|
char temp_str_buf[number::ObNumber::MAX_PRINTABLE_SIZE];
|
|
if (snprintf(temp_str_buf, sizeof(temp_str_buf), "Name_exp_%ld", auto_name_id_++) < 0) {
|
|
ret = OB_SIZE_OVERFLOW;
|
|
SQL_RESV_LOG(WARN, "failed to generate buffer for temp_str_buf", K(ret));
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
tmp_col_name = ObString::make_string(temp_str_buf);
|
|
if (OB_FAIL(ob_write_string(*allocator_, tmp_col_name, col_name))) {
|
|
SQL_RESV_LOG(WARN, "Can not malloc space for constraint name", K(ret));
|
|
} else {
|
|
select_item.alias_name_.assign_ptr(col_name.ptr(), col_name.length());
|
|
}
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && OB_FAIL(ObSQLUtils::check_column_name(cs_type, select_item.alias_name_))) {
|
|
LOG_WARN("fail to make field name", K(ret));
|
|
}
|
|
if (OB_SUCC(ret) && OB_FAIL(select_stmt->add_select_item(select_item))) {
|
|
LOG_WARN("add select item to select stmt failed", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObViewTableResolver::resolve_subquery_info(const ObIArray<ObSubQueryInfo> &subquery_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(session_info_)) {
|
|
ret = OB_NOT_INIT;
|
|
LOG_WARN("session info is null");
|
|
} else if (current_level_ + 1 >= OB_MAX_SUBQUERY_LAYER_NUM && subquery_info.count() > 0) {
|
|
ret = OB_NOT_SUPPORTED;
|
|
LOG_USER_ERROR(OB_NOT_SUPPORTED, "too many levels of subqueries");
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < subquery_info.count(); i++) {
|
|
const ObSubQueryInfo &info = subquery_info.at(i);
|
|
ObViewTableResolver subquery_resolver(params_, view_db_name_, view_name_);
|
|
subquery_resolver.set_current_level(current_level_ + 1);
|
|
subquery_resolver.set_current_view_level(current_view_level_);
|
|
subquery_resolver.set_parent_namespace_resolver(this);
|
|
subquery_resolver.set_parent_view_resolver(parent_view_resolver_);
|
|
subquery_resolver.set_current_view_item(current_view_item);
|
|
set_query_ref_expr(info.ref_expr_);
|
|
if (OB_FAIL(add_cte_table_to_children(subquery_resolver))) {
|
|
LOG_WARN("add CTE table to children failed", K(ret));
|
|
} else if (is_only_full_group_by_on(session_info_->get_sql_mode())) {
|
|
subquery_resolver.set_parent_aggr_level(info.parents_expr_info_.has_member(IS_AGG) ?
|
|
current_level_ : parent_aggr_level_);
|
|
}
|
|
if (OB_FAIL(do_resolve_subquery_info(info, subquery_resolver))) {
|
|
LOG_WARN("do resolve subquery info failed", K(ret));
|
|
}
|
|
set_query_ref_expr(NULL);
|
|
}
|
|
return ret;
|
|
}
|
|
} // namespace sql
|
|
} // namespace oceanbase
|