Files
oceanbase/src/sql/plan_cache/ob_sql_parameterization.cpp

2346 lines
102 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_PC
#include "ob_sql_parameterization.h"
#include "share/schema/ob_schema_struct.h"
#include "lib/json/ob_json_print_utils.h"
#include "sql/engine/ob_exec_context.h"
#include "sql/parser/ob_parser.h"
#include "sql/parser/ob_fast_parser.h"
#include "sql/resolver/ob_resolver_utils.h"
#include "sql/parser/parse_malloc.h"
#include "sql/ob_sql_utils.h"
#include "common/ob_smart_call.h"
#include <algorithm>
using namespace oceanbase;
using namespace sql;
using namespace common;
using namespace share::schema;
SqlInfo::SqlInfo() :
total_(0),
last_active_time_(0),
hit_count_(0),
ps_need_parameterized_(true),
need_check_fp_(false)
{
}
namespace oceanbase {
namespace sql {
struct TransformTreeCtx
{
ObIAllocator *allocator_;
ParseNode *top_node_;
ParseNode *tree_;
ObStmtScope expr_scope_;
int64_t value_father_level_;
ObCollationType collation_type_;
ObCollationType national_collation_type_;
const ObTimeZoneInfo *tz_info_;
int64_t question_num_;
ParamStore *params_;
ObMaxConcurrentParam::FixParamStore *fixed_param_store_;
bool not_param_; //表示该节点及其子节点是常量时也不能参数化
bool is_fast_parse_const_; //表示当前node是否为fp可识别的常量
bool enable_contain_param_;//表示该节点及其子节点中常量是否为fp可识别的参数。
SqlInfo *sql_info_;
int64_t paramlized_questionmask_count_;//表示该查询中可以被参数化的?个数,用于sql限流
bool is_transform_outline_;//是否在resolve outline, 用于sql限流
ObLengthSemantics default_length_semantics_;
ObSEArray<void*, 16> project_list_; // 记录下所有T_PROJECT_STRING节点
const ObIArray<ObPCParam *> *raw_params_;
SQL_EXECUTION_MODE mode_;
bool is_project_list_scope_;
int64_t assign_father_level_;
const ObIArray<FixedParamValue> *udr_fixed_params_;
bool ignore_scale_check_;
bool is_from_pl_;
TransformTreeCtx();
};
struct SelectItemTraverseCtx
{
const common::ObIArray<ObPCParam *> &raw_params_;
const ParseNode *tree_;
const ObString &org_expr_name_;
const int64_t expr_start_pos_;
const int64_t buf_len_;
int64_t &expr_pos_;
SelectItemParamInfo &param_info_;
SelectItemTraverseCtx(const common::ObIArray<ObPCParam *> &raw_params,
const ParseNode *tree,
const ObString &org_expr_name,
const int64_t expr_start_pos,
const int64_t buf_len,
int64_t &expr_pos,
SelectItemParamInfo &param_info)
: raw_params_(raw_params), tree_(tree), org_expr_name_(org_expr_name),
expr_start_pos_(expr_start_pos), buf_len_(buf_len), expr_pos_(expr_pos), param_info_(param_info) {}
};
struct TraverseStackFrame
{
const ParseNode *cur_node_;
int64_t next_child_idx_;
TO_STRING_KV(K_(next_child_idx),
K(cur_node_->type_));
};
}
}
TransformTreeCtx::TransformTreeCtx() :
allocator_(NULL),
top_node_(NULL),
tree_(NULL),
expr_scope_(T_NONE_SCOPE),
value_father_level_(ObSqlParameterization::NO_VALUES),
collation_type_(),
national_collation_type_(CS_TYPE_INVALID),
tz_info_(NULL),
question_num_(0),
params_(NULL),
fixed_param_store_(NULL),
not_param_(false),
is_fast_parse_const_(false),
enable_contain_param_(true),
sql_info_(NULL),
paramlized_questionmask_count_(0),
is_transform_outline_(false),
default_length_semantics_(LS_BYTE),
raw_params_(NULL),
mode_(INVALID_MODE),
is_project_list_scope_(false),
assign_father_level_(ObSqlParameterization::NO_VALUES),
udr_fixed_params_(NULL),
ignore_scale_check_(false),
is_from_pl_(false)
{
}
// replace const expr with ? in syntax tree
// seprate params from syntax tree
int ObSqlParameterization::transform_syntax_tree(ObIAllocator &allocator,
const ObSQLSessionInfo &session,
const ObIArray<ObPCParam *> *raw_params,
ParseNode *tree,
SqlInfo &sql_info,
ParamStore &params,
SelectItemParamInfoArray *select_item_param_infos,
ObMaxConcurrentParam::FixParamStore &fixed_param_store,
bool is_transform_outline,
SQL_EXECUTION_MODE execution_mode,
const ObIArray<FixedParamValue> *udr_fixed_params,
bool is_from_pl)
{
int ret = OB_SUCCESS;
ObCollationType collation_connection = CS_TYPE_INVALID;
ParseNode *children_node = tree->children_[0];
if (OB_ISNULL(tree)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(tree), K(ret));
} else if (OB_ISNULL(children_node)){
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(tree), K(ret));
} else if (OB_FAIL(session.get_collation_connection(collation_connection))) {
SQL_PC_LOG(WARN, "fail to get collation_connection", K(ret));
} else {
sql_info.sql_traits_.stmt_type_ = children_node->type_;
TransformTreeCtx ctx;
ctx.collation_type_ = collation_connection;
ctx.national_collation_type_ = session.get_nls_collation_nation();
ctx.tz_info_ = session.get_timezone_info();
ctx.default_length_semantics_ = session.get_actual_nls_length_semantics();
ctx.allocator_ = &allocator;
ctx.tree_ = tree;
ctx.top_node_ = tree;
ctx.expr_scope_ = T_NONE_SCOPE;
ctx.value_father_level_ = NO_VALUES;
ctx.question_num_ = 0;
ctx.params_ = &params;
ctx.fixed_param_store_ = &fixed_param_store;
ctx.not_param_ = false;
ctx.is_fast_parse_const_ = false;
ctx.enable_contain_param_ = true;
ctx.sql_info_ = &sql_info;
ctx.paramlized_questionmask_count_ = 0;//used for outline sql限流,
ctx.is_transform_outline_ = is_transform_outline;//used for outline sql限流
ctx.raw_params_ = raw_params;
ctx.udr_fixed_params_ = udr_fixed_params;
ctx.is_project_list_scope_ = false;
ctx.mode_ = execution_mode;
ctx.assign_father_level_ = NO_VALUES;
ctx.is_from_pl_ = is_from_pl;
if (OB_FAIL(transform_tree(ctx, session))) {
if (OB_NOT_SUPPORTED != ret) {
SQL_PC_LOG(WARN, "fail to transform syntax tree", K(ret));
}
} else if (is_transform_outline && (INT64_MAX != children_node->value_)
&& (children_node->value_ != ctx.paramlized_questionmask_count_)) {
ret = OB_INVALID_OUTLINE;
LOG_USER_ERROR(OB_INVALID_OUTLINE, "? appears at invalid position in sql_text");
SQL_PC_LOG(WARN, "? appear at invalid position", "total count of questionmasks in query", children_node->value_,
"paramlized_questionmask_count", ctx.paramlized_questionmask_count_, K(ret));
} else if (OB_NOT_NULL(raw_params)
&& OB_NOT_NULL(select_item_param_infos)) {
if (sql_info.total_ != raw_params->count()) {
ret = OB_NOT_SUPPORTED;
SQL_PC_LOG(TRACE, "const number of fast parse and normal parse is different",
"fast_parse_const_num", raw_params->count(),
"normal_parse_const_num", sql_info.total_,
K(session.get_current_query_string()),
"result_tree_", SJ(ObParserResultPrintWrapper(*ctx.top_node_)));
} else {
select_item_param_infos->set_capacity(ctx.project_list_.count());
for (int64_t i = 0; OB_SUCC(ret) && i < ctx.project_list_.count(); i++) {
ParseNode *tmp_root = static_cast<ParseNode *>(ctx.project_list_.at(i));
if (OB_ISNULL(tmp_root)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid null child", K(ret), K(i), K(ctx.project_list_.at(i)));
} else if (0 == tmp_root->is_val_paramed_item_idx_
&& OB_FAIL(get_select_item_param_info(*raw_params,
tmp_root,
select_item_param_infos))) {
SQL_PC_LOG(WARN, "failed to get select item param info", K(ret));
} else {
// do nothing
}
} // for end
}
}
SQL_PC_LOG(DEBUG, "after transform_tree",
"result_tree_", SJ(ObParserResultPrintWrapper(*tree)));
}
return ret;
}
//判断是否是fast parse能识别的常量(只考虑本node)
//
//T_CAST_ARGUMENT,T_FUN_SYS_CUR_TIMESTAMP, T_SFU_INT
//该类型node在快速参数化时是常数(eg:cast ( 10 AS CHAR(20)中20, now(10));
//但正常parse时node结点类型为不在常数范围,
//该node不需要参数化,但为了保证正常parse和fast parse常量个数匹配,所以加入特殊判断;
//该参数在正常parse时处理方式和order by中参数相同;
//
//T_SFU_INT 和T_FUN_SYS_UTC_TIME时判段为0 或-1,
//是因为now()和FOR UPDATE没有参数,但语法会给默认值,加判断是为了让正常parse不认为该值为常量。
int ObSqlParameterization::is_fast_parse_const(TransformTreeCtx &ctx)
{
int ret = OB_SUCCESS;
if (NULL == ctx.tree_) { //parse tree中存在node为NULL
ctx.is_fast_parse_const_ = false;
} else {
//如果其节点为T_HINT_OPTION_LIST则该节点及其子节点中常量都不是fp可识别的参数参数。
if (T_HINT_OPTION_LIST == ctx.tree_->type_) {
ctx.enable_contain_param_ = false;
}
if (ctx.enable_contain_param_) {
//当该node为T_NULL/T_VARCHAR类型且is_hidden_const_为true时表示该node在fast parse中不能识别为常量
if ((T_NULL == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_VARCHAR == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_INT == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_CAST_ARGUMENT == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_DOUBLE == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_IEEE754_INFINITE == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_IEEE754_NAN == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_SFU_INT == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| (T_FLOAT == ctx.tree_->type_ && true == ctx.tree_->is_hidden_const_)
|| true == ctx.tree_->is_forbid_parameter_) {
ctx.is_fast_parse_const_ = false;
} else {
ctx.is_fast_parse_const_ = (IS_DATATYPE_OP(ctx.tree_->type_)
|| T_QUESTIONMARK == ctx.tree_->type_
|| T_COLLATION == ctx.tree_->type_
|| T_CAST_ARGUMENT == ctx.tree_->type_
|| T_NULLX_CLAUSE == ctx.tree_->type_
|| (T_SFU_INT == ctx.tree_->type_ && -1 != ctx.tree_->value_)
|| T_SFU_DECIMAL == ctx.tree_->type_
|| T_WEIGHT_STRING_LEVEL_PARAM == ctx.tree_->type_);
}
} else {
ctx.is_fast_parse_const_ = false;
}
}
return ret;
}
bool ObSqlParameterization::is_udr_not_param(TransformTreeCtx &ctx)
{
bool b_ret = false;
if (OB_ISNULL(ctx.tree_) || (NULL == ctx.udr_fixed_params_)) {
b_ret = false;
} else {
for (int64_t i = 0; !b_ret && i < ctx.udr_fixed_params_->count(); ++i) {
const FixedParamValue &fixed_param = ctx.udr_fixed_params_->at(i);
if (fixed_param.idx_ == ctx.tree_->raw_param_idx_) {
b_ret = true;
break;
}
}
}
return b_ret;
}
//判断该node是否为不能参数化的node
bool ObSqlParameterization::is_node_not_param(TransformTreeCtx &ctx)
{
bool not_param = false;
if (NULL == ctx.tree_) {
not_param = true;
} else if (!ctx.is_fast_parse_const_) {
not_param = true;
} else if (ctx.not_param_) { //如果其本身或其祖先已经判定了所有子节点为not param, 则为not param
not_param = true;
} else {
not_param = false;
}
return not_param;
}
//判断该节点及其子节点是常量时也不能参数化。
bool ObSqlParameterization::is_tree_not_param(const ParseNode *tree)
{
bool ret_bool = false;
if (NULL == tree) {
ret_bool = true;
} else if (true == tree->is_tree_not_param_) {
ret_bool = true;
} else if (lib::is_mysql_mode() && T_GROUPBY_CLAUSE == tree->type_) {
// oracle模式下,select a from t group by 1这种语法被禁止,所以可以开放group by的参数化
ret_bool = true;
} else if (T_SORT_LIST == tree->type_) {
ret_bool = true;
} else if (T_HINT_OPTION_LIST == tree->type_) {
ret_bool = true;
} else if (T_COLLATION == tree->type_) {
ret_bool = true;
} else if (T_CAST_ARGUMENT == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_UTC_TIMESTAMP == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_UTC_TIME == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_SYSDATE == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_SYSTIMESTAMP == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_CUR_TIMESTAMP == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_LOCALTIMESTAMP == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_CUR_TIME == tree->type_) {
ret_bool = true;
} else if (T_FUN_SYS_CUR_DATE == tree->type_) {
ret_bool = true;
} else if (T_SFU_INT == tree->type_ ||
T_SFU_DECIMAL == tree->type_ ||
T_SFU_DOUBLE == tree->type_) {
ret_bool = true;
} else if (T_CYCLE_NODE == tree->type_) {
ret_bool = true;
} else if (T_INTO_FIELD_LIST == tree->type_) {
ret_bool = true;
} else if (T_INTO_LINE_LIST == tree->type_) {
ret_bool = true;
} else if (T_PIVOT_IN_LIST == tree->type_) {
ret_bool = true;
} else if (T_CHAR_CHARSET == tree->type_) {
ret_bool = true;
} else if (T_WIN_NAMED_WINDOWS == tree->type_) {//name window无法参数化,因为无法保证其参数化顺序
ret_bool = true;
} else {
// do nothing
}
return ret_bool;
}
SQL_EXECUTION_MODE ObSqlParameterization::get_sql_execution_mode(ObPlanCacheCtx &pc_ctx)
{
SQL_EXECUTION_MODE mode = INVALID_MODE;
if (PC_PS_MODE == pc_ctx.mode_ || PC_PL_MODE == pc_ctx.mode_) {
if (pc_ctx.is_parameterized_execute_) {
mode = (PC_PL_MODE == pc_ctx.mode_) ? PL_EXECUTE_MODE : PS_EXECUTE_MODE;
} else {
mode = (PC_PL_MODE == pc_ctx.mode_) ? PL_PREPARE_MODE : PS_PREPARE_MODE;
}
} else {
mode = TEXT_MODE;
}
return mode;
}
bool ObSqlParameterization::is_prepare_mode(SQL_EXECUTION_MODE mode)
{
return (PS_PREPARE_MODE == mode || PL_PREPARE_MODE == mode);
}
bool ObSqlParameterization::is_execute_mode(SQL_EXECUTION_MODE mode)
{
return (PS_EXECUTE_MODE == mode || PL_EXECUTE_MODE == mode);
}
/* fix:
* decide if a number param can ignore scale check when choosing plancache.
* if current node type is expr list or number,
* and parent node is point, st_point, json array, or ctx.ignore_scale_check_ is true, return true,
* otherwise return false.
* example1 scale check could ignore:
* 'select point(1.1, 1.1)' and 'select point(1.1111, 1.1111)' could share a same plancache,
* scale check about number literal in them are ignored
* example2 scale check cannot ignore:
* 'select point(cast(1.11 as double), 1.1)' and 'select point(cast(1.1111 as double), 1.1111)'
* because 1.11 and 1.1111 in cast expr have different scales,
* scale check will prevent them to share a same plancache
*/
bool ObSqlParameterization::is_ignore_scale_check(TransformTreeCtx &ctx, const ParseNode *parent)
{
bool ret_bool = false;
if (OB_ISNULL(parent) || OB_ISNULL(ctx.tree_)) {
// do nothing
} else if (T_EXPR_LIST == ctx.tree_->type_ || T_NUMBER == ctx.tree_->type_) {
if (T_NUMBER == ctx.tree_->type_ && ctx.ignore_scale_check_) {
ret_bool = true;
} else if (T_FUN_SYS_POINT == parent->type_) {
ret_bool = true;
} else if (T_FUN_SYS == parent->type_) {
ParseNode **node = parent->children_;
if (OB_ISNULL(node) || OB_ISNULL(node[0])) {
// do nothing
} else {
ObString func_name(node[0]->str_len_, node[0]->str_value_);
if ((0 == func_name.case_compare("st_point"))) {
ret_bool = true;
}
}
} else { /* do nothing*/ }
} else { /* do nothing*/ }
return ret_bool;
}
// helper method of transform_syntax_tree
//正常parse和fast parse识别常量不一致的问题
/*
* 1.replace/insert的列带括号为空时导致正常parse和fast parse识别常量不一致
* eg:replace into t2() values(40, 1, 'good')
* 原因:主要是因为opt_insert_columns,含括号,但为空时,会默认生成T_NULL节点,导致正常parse和fast parse识别常量不一致。
* 解法:将T_NULL类型改成T_EMPTY类型即可,在resolver阶段并没有对该类型识别,而是使用这种判断进行处理if (T_COLUMN_LIST ==)else {//处理为空的情况}
*2.default()导致的正常parse和fast parse常量不一致
* eg :select * from type_t where int_c = default(int_c)
* 原因:default()在语法阶段生成4个T_NULL类型的null_node,在resolve阶段会填充具体的内容,导致不一致。
* 解法:将生成的null_node中is_hidden_const_字段置1, 在transform tree阶段,判断node为T_NULL类型且is_hidden_const_为true时表示该node在fast parse中不能识别为常量,从而不参数化,保证正常parse和fast parse识别常量一致。
*3.using charset_name导致的正常parse和fast parse识别常量不一致
* eg:select convert('1234' using 'utf8’);
* eg:select convert('1234' using utf8);
* 原因:using ‘utf8’ 和using utf8均会生成都会生成一个T_VARCHAR类型的节点,对于using utf8, fast parse不能识别出常量,因此导致正常parse和fast parse识别不一致。
* 解法:将using utf8生成的T_VARCHAR节点中is_hidden_const_字段置1,在transform tree阶段,判断node为T_VARCHAR类型且is_hidden_const_为true时表示该node在fast parse中不能识别为常量,从而不参数化,保证正常parse和fast parse识别常量一致。
*4.month等date_unit导致的正常parse和fast parse识别常量个数不一致
* eg:SELECT month(NULL);
* select * from type_t where date_c = date_add('2015-01-01', interval 5 month)
* 问题:在语法阶段,mouth,day等date_unit是解析为T_INT类型的节点,导致正常parse与fast parse识别不一致。之前的方案是将mouth等date_unit在词法阶段识别出来,并生成对应的 T_INT类型节点解决该问题。但这导致mouth()作为函数或当sql中存在以month等date_unit命名的table或unit时会导致正常parse与fast parse识别常量不一致的问题。
* 解法:将month,day等date_unit生成的T_INT节点中is_hidden_const_字段置1,在transform tree阶段,判断node为T_INT类型且is_hidden_const_为true时表示该node在fast parse中不能识别为常量,从而不参数化,保证正常parse和fast parse识别常量一致。
*5.as STRING_VALUE导致的正常parse和fast parse识别常量不一致
* eg: select 'abc' as a;
* select 'abc' as ‘a’;
* 问题:as a 和 as ‘a’ 在语法阶段会生成T_ALIAS节点,导致对于as ‘a’在fast parse能识别一个常量,而正常parse不能识别出常量。
* 解法:在T_ALIAS节点中记录对应的fast parse能够识别的常量个数(0个或1个),在transform时,如果是T_ALIAS节点,则将其判断为不能参数化的常量,并将对应的常量个数记下,从而保证下次fast parse时能够识别的常量的个数一致。
*
*6. 问题: 当函数中存在格式字符串时, 格式字符串不需要参数化, eg:STR_TO_DATE, FROM_UNIXTIME, DATE_FORMAT
* 解法: 在参数化过程中,遍历parse tree,识别到T_FUN_SYS节点后,识别第一个children(函数名),然后遍历所有child,并对含格式串的函数中格式化参数节点及其子节点(该格式串可能由函数生成)均做不需要参数化标记。
*
* 7. 问题: weight_string函数在包含level参数时,level可以是变长的、结构化的数据,此时normal_parser会将level的常数进行扁平化处理(只有一个T_INT类型的ParseNode),但是fast_parser会识别到所有的数字,导致两者常量数量不一致
* eg:
* select weight_string("AAA" as char(1) level 1-2 );
* select weight_string("AAA" as char(1) level 1,2,3,4,5,6,7,8,9,10,11 );
* select weight_string("AAA" as char(1) level 1 desc,2 asc ,3 desc ,4 reverse,5,6,7,8,9 reverse,10,11 );
* 解法:创建一个新的item_type(T_WEIGHT_STRING_LEVEL_PARAM) ,根据不同的语法来设置不同的param_num_,这样normal_parser就可以和faster_parser相等了,等到具体处理T_WEIGHT_STRING_LEVEL_PARAM的时候,把它转换为T_INT进行后续的处理。
*/
int ObSqlParameterization::transform_tree(TransformTreeCtx &ctx,
const ObSQLSessionInfo &session_info)
{
int ret = OB_SUCCESS;
int64_t value_level = NO_VALUES;
int64_t assign_level = NO_VALUES;
if (OB_ISNULL(ctx.top_node_)
|| OB_ISNULL(ctx.allocator_)
|| OB_ISNULL(ctx.sql_info_)
|| OB_ISNULL(ctx.fixed_param_store_)
|| OB_ISNULL(ctx.params_)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "top node is NULL",
K(ctx.top_node_),
K(ctx.allocator_),
K(ctx.sql_info_),
K(ctx.fixed_param_store_),
K(ctx.params_),
K(ret));
} else if (NULL == ctx.tree_) {
// do nothing
} else {
ParseNode *func_name_node = NULL;
if (T_WHERE_SCOPE == ctx.expr_scope_ && T_FUN_SYS == ctx.tree_->type_) {
if (OB_ISNULL(ctx.tree_->children_)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ctx.tree_->children_), K(ret));
} else if (NULL == (func_name_node = ctx.tree_->children_[0])) {
ret = OB_ERR_UNEXPECTED;
SQL_PC_LOG(ERROR, "function name node is NULL", K(ret));
} else {
ObString func_name(func_name_node->str_len_, func_name_node->str_value_);
ObString func_name_is_serving_tenant(N_IS_SERVING_TENANT);
if (func_name == func_name_is_serving_tenant) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("is_serving_tenant is not supported", K(ret));
}
}
}
if (OB_SUCC(ret) && T_PROJECT_STRING == ctx.tree_->type_
&& OB_FAIL(ctx.project_list_.push_back(ctx.tree_))) {
LOG_WARN("failed to push back element", K(ret));
} else {
// do nothing
}
if (OB_SUCC(ret)) {
ObObjParam value;
ObAccuracy tmp_accuracy;
bool is_fixed = true;
if (ctx.is_fast_parse_const_) { //这里面需要获取fast parse识别为常量的所有信息
if (!is_node_not_param(ctx)) { //判定是否为可以参数化的常量
//for sql 限流
ParseNode* node = NULL;
if (OB_NOT_NULL(ctx.raw_params_) &&
ctx.tree_->value_ < ctx.raw_params_->count() &&
T_QUESTIONMARK == ctx.tree_->type_ &&
!is_prepare_mode(ctx.mode_)) {
node = ctx.raw_params_->at(ctx.tree_->value_)->node_;
} else {
node = ctx.tree_;
}
if (T_QUESTIONMARK == ctx.tree_->type_) {
ctx.paramlized_questionmask_count_++;
is_fixed = false;
}
ObString literal_prefix;
int64_t server_collation = CS_TYPE_INVALID;
if (lib::is_oracle_mode() &&OB_FAIL(
session_info.get_sys_variable(share::SYS_VAR_COLLATION_SERVER, server_collation))) {
LOG_WARN("get sys variable failed", K(ret));
} else if (OB_FAIL(add_param_flag(ctx.tree_, *ctx.sql_info_))) {
SQL_PC_LOG(WARN, "fail to get neg flag", K(ret));
} else if (OB_FAIL(ObResolverUtils::resolve_const(node,
static_cast<stmt::StmtType>(ctx.sql_info_->sql_traits_.stmt_type_),
*(ctx.allocator_),
ctx.collation_type_,
ctx.national_collation_type_,
ctx.tz_info_,
value,
ctx.is_transform_outline_,
literal_prefix,
ctx.default_length_semantics_,
static_cast<ObCollationType>(server_collation),
NULL, session_info.get_sql_mode(), ctx.is_from_pl_))) {
SQL_PC_LOG(WARN, "fail to resolve const", K(ret));
} else {
//对于字符串值,其T_VARCHAR型的parse node有一个T_VARCHAR类型的子node,该子node描述字符串的charset等信息。
//因此对于可参数化的参数,当该节点的父节点为T_VALUE_VECTOR, 且类型为T_VARCHAR或没有子node时,认为是单值(不属于复杂表达式中参数);
//此时将value中单值参数标记为不需要在plan cache中强匹配类型的参数
if ((VALUE_VECTOR_LEVEL == ctx.value_father_level_
|| ASSIGN_ITEM_LEVEL == ctx.assign_father_level_)
&& (0 == ctx.tree_->num_child_
|| T_VARCHAR == ctx.tree_->type_
|| (lib::is_oracle_mode() && T_CHAR == ctx.tree_->type_)
|| T_QUESTIONMARK == ctx.tree_->type_)) {
if (T_QUESTIONMARK == ctx.tree_->type_) {
if (OB_FAIL(ctx.sql_info_->no_check_type_offsets_.push_back(ctx.tree_->value_))) {
SQL_PC_LOG(WARN, "failed to add no check type offsets", K(ret));
}
} else {
value.set_need_to_check_type(false);
}
} else {
if (T_QUESTIONMARK == ctx.tree_->type_) {
if (OB_FAIL(ctx.sql_info_->need_check_type_param_offsets_.add_member(ctx.tree_->value_))) {
SQL_PC_LOG(WARN, "failed to add member", K(ctx.tree_->value_));
}
} else {
value.set_need_to_check_type(true);
}
if (ctx.ignore_scale_check_) {
value.set_ignore_scale_check(true);
}
}
//用于sql限流,记录哪些参数需要严格比对
if (OB_SUCC(ret) && is_fixed) {
ObFixedParam fix_param;
fix_param.offset_ = ctx.params_->count();
fix_param.value_ = value;
if (OB_FAIL(ctx.fixed_param_store_->push_back(fix_param))) {
SQL_PC_LOG(WARN, "fail to push back fix params", K(fix_param), K(ret));
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(add_varchar_charset(ctx.tree_, *ctx.sql_info_))) {
SQL_PC_LOG(WARN, "fail to add varchar charset", K(ret));
}
}
if (OB_SUCC(ret) && ctx.tree_->type_ != T_QUESTIONMARK) {
if (OB_FAIL(ctx.sql_info_->fixed_param_idx_.push_back(ctx.question_num_))) {
SQL_PC_LOG(WARN, "failed to add question mark idx", K(ret));
}
}
ctx.tree_->is_literal_bool_ = (T_BOOL == ctx.tree_->type_);
value.set_is_boolean(value.is_boolean() || ctx.tree_->is_literal_bool_);
if (T_QUESTIONMARK != ctx.tree_->type_ && ctx.is_project_list_scope_) {
ctx.sql_info_->ps_need_parameterized_ = false;
}
// If it is internal sql, such as pl internal sql, the formatted text string is used.
// because it is necessary to replace the sql variable in pl with the standard ps text
// for hard parsing. In this case, the param store has been constructed in advance
// so the constant cannot be resolved as a question mark, and there is no need to
// add it to the param store.
ObItemType node_type;
if (!is_execute_mode(ctx.mode_)) {
node_type = ctx.tree_->type_;
ctx.tree_->type_ = T_QUESTIONMARK;
ctx.tree_->raw_param_idx_ = ctx.sql_info_->total_;
ctx.tree_->value_ = ctx.question_num_;
}
ctx.question_num_++;
ctx.sql_info_->total_++;
if (1 == ctx.tree_->is_num_must_be_pos_ && OB_SUCC(ret)
&& OB_FAIL(ctx.sql_info_->must_be_positive_index_.add_member(ctx.tree_->raw_param_idx_))) {
LOG_WARN("failed to add bitset member", K(ret));
}
if (OB_NOT_NULL(ctx.tree_) &&
OB_NOT_NULL(ctx.raw_params_) &&
ctx.tree_->raw_param_idx_ >= 0 &&
ctx.tree_->raw_param_idx_ < ctx.raw_params_->count() &&
OB_NOT_NULL(ctx.raw_params_->at(ctx.tree_->raw_param_idx_))) {
const ParseNode *node = ctx.raw_params_->at(ctx.tree_->raw_param_idx_)->node_;
value.set_raw_text_info(static_cast<int32_t>(node->raw_sql_offset_),
static_cast<int32_t>(node->text_len_));
if (ctx.sql_info_->need_check_fp_) {
ObPCParseInfo p_info;
p_info.param_idx_ = ctx.sql_info_->total_ - 1;
p_info.flag_ = NORMAL_PARAM;
p_info.raw_text_pos_ = ctx.tree_->sql_str_off_;
if (ctx.tree_->sql_str_off_ == -1) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("invlid str off", K(lbt()), K(ctx.tree_),
K(ctx.tree_->raw_param_idx_), K(get_type_name(node_type)),
K(session_info.get_current_query_string()),
"result_tree_", SJ(ObParserResultPrintWrapper(*ctx.top_node_)));
}
if (OB_FAIL(ret)) {
// do nothing
} else if (OB_FAIL(ctx.sql_info_->parse_infos_.push_back(p_info))) {
SQL_PC_LOG(WARN, "fail to push parser info", K(ret));
}
}
}
if (OB_FAIL(ret)) {
//do nothing
} else if (!is_execute_mode(ctx.mode_) && OB_FAIL(ctx.params_->push_back(value))) {
SQL_PC_LOG(WARN, "fail to push into params", K(ret));
} else if (is_udr_not_param(ctx) && OB_FAIL(add_not_param_flag(ctx.tree_, *ctx.sql_info_))) {
SQL_PC_LOG(WARN, "fail to add not param flag", K(ret));
}
}
} else if (OB_FAIL(add_not_param_flag(ctx.tree_, *ctx.sql_info_))) { //not param
SQL_PC_LOG(WARN, "fail to add not param flag", K(ret));
}
if (ctx.sql_info_->need_check_fp_ && ret == OB_NOT_SUPPORTED) {
LOG_WARN("print tree", K(session_info.get_current_query_string()),
"result_tree_", SJ(ObParserResultPrintWrapper(*ctx.top_node_)));
}
} //if is_fast_parse_const end
}
// sql with charset need not ps parameterize
if (OB_SUCC(ret) && T_QUESTIONMARK == ctx.tree_->type_ && OB_NOT_NULL(ctx.tree_->children_)
&& OB_NOT_NULL(ctx.tree_->children_[0]) && ctx.tree_->children_[0]->type_ == T_CHARSET) {
ctx.sql_info_->ps_need_parameterized_ = false;
}
//判断insert中values()在tree中的哪一层,当某结点value_father_level_处于VALUE_VECTOR_LEVEL时,
//便可通过判断该节点的num_child_数是否为0,来判断values中的项是否为复杂表达式;
if (OB_SUCC(ret)) {
if (T_VALUE_LIST == ctx.tree_->type_) {
value_level = VALUE_LIST_LEVEL;
} else if (T_VALUE_VECTOR == ctx.tree_->type_
&& VALUE_LIST_LEVEL == ctx.value_father_level_) {
value_level = VALUE_VECTOR_LEVEL;
} else if (ctx.value_father_level_ >= VALUE_VECTOR_LEVEL) {
value_level = ctx.value_father_level_ + 1;
}
}
if (OB_SUCC(ret)) {
if (T_ASSIGN_LIST == ctx.tree_->type_) {
assign_level = ASSIGN_LIST_LEVEL;
} else if (T_ASSIGN_ITEM == ctx.tree_->type_
&& ASSIGN_LIST_LEVEL == ctx.assign_father_level_) {
assign_level = ASSIGN_ITEM_LEVEL;
} else if (ctx.assign_father_level_ >= ASSIGN_ITEM_LEVEL) {
assign_level = ctx.assign_father_level_ + 1;
}
}
// transform `operand - const_num_val` to `operand + (-const_num_val)`
if (OB_SUCC(ret) && OB_FAIL(transform_minus_op(*(ctx.allocator_), ctx.tree_, ctx.is_from_pl_))) {
LOG_WARN("failed to transform minums operation", K(ret));
} else if (lib::is_oracle_mode()) {
// in oracle mode, select +-1 from dual is prohibited, but with following orders, it can be executed successfully:
// 1. select +1 from dual; (genereted plan with key: select +? from dual)
// 2. select +-1 from dual; (hit plan before, executed successfully)
// Thus, add a constraint in plan cache: the numeric value following `-` or `+` operators must be posbitive number
if (T_OP_POS == ctx.tree_->type_ || T_OP_NEG == ctx.tree_->type_ ) {
if (OB_ISNULL(ctx.tree_->children_)
|| ctx.tree_->num_child_ != 1
|| OB_ISNULL(ctx.tree_->children_[0])) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid syntax tree", K(ret));
} else if (ob_is_numeric_type(ITEM_TO_OBJ_TYPE(ctx.tree_->children_[0]->type_))) {
ctx.tree_->children_[0]->is_num_must_be_pos_ = 1;
} else {
// do nothing
}
} else {
// do nothing
}
}
if (T_LIMIT_CLAUSE == ctx.tree_->type_) {
// limit a offset b, a and b must be postive
// 0 is counted as positive, -0 is counted as negative
if (OB_ISNULL(ctx.tree_->children_) || 2 != ctx.tree_->num_child_) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid syntax tree", K(ret),
K(ctx.tree_->children_), K(ctx.tree_->num_child_));
} else if (OB_NOT_NULL(ctx.tree_->children_[0])
&& ob_is_numeric_type(ITEM_TO_OBJ_TYPE(ctx.tree_->children_[0]->type_))
&& FALSE_IT(ctx.tree_->children_[0]->is_num_must_be_pos_ = 1)) {
} else if (OB_NOT_NULL(ctx.tree_->children_[1])
&& ob_is_numeric_type(ITEM_TO_OBJ_TYPE(ctx.tree_->children_[1]->type_))
&& FALSE_IT(ctx.tree_->children_[1]->is_num_must_be_pos_ = 1)) {
}
} else if (T_COMMA_LIMIT_CLAUSE == ctx.tree_->type_) {
// limit a, b, a and b must be postive
if (OB_ISNULL(ctx.tree_->children_)
|| 2 != ctx.tree_->num_child_
|| OB_ISNULL(ctx.tree_->children_[0])
|| OB_ISNULL(ctx.tree_->children_[1])) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid syntax tree", K(ret));
} else if (ob_is_numeric_type(ITEM_TO_OBJ_TYPE(ctx.tree_->children_[0]->type_))
&& FALSE_IT(ctx.tree_->children_[0]->is_num_must_be_pos_ = 1)) {
} else if (ob_is_numeric_type(ITEM_TO_OBJ_TYPE(ctx.tree_->children_[1]->type_))
&& FALSE_IT(ctx.tree_->children_[1]->is_num_must_be_pos_ = 1)) {
}
}
bool enable_contain_param = ctx.enable_contain_param_;
ParseNode *root = ctx.tree_;
//当type为T_QUESTIONMARK时不需要再考虑子节点的参数化,
//对于select '1'的T_VARCHAR和T_CHAR(oracle模式下) node有数据一样的T_VARCHAR子节点,
//由于select中投影列不需要参数化,所以会导致正常parse识别有两个常量,
//而fast parse只识别一个常量,所以在此加T_VARCHAR的判断, 使得两种parse均只能识别一个常量。
bool not_param = ctx.not_param_;
if (not_param) {
ctx.sql_info_->ps_need_parameterized_ = false;
}
bool is_project_list_scope = T_PROJECT_LIST == root->type_;
if (is_project_list_scope) {
ctx.is_project_list_scope_ = true;
}
bool ignore_scale_check = ctx.ignore_scale_check_;
for (int32_t i = 0;
OB_SUCC(ret) && i < root->num_child_ && root->type_ != T_QUESTIONMARK
&& root->type_ != T_VARCHAR && root->type_ != T_CHAR && root->type_ != T_NCHAR;
++i) {
//如果not_param本来就是true则不需要再判断;因为某结点判断为true,则该结点子树均为true;
if (OB_ISNULL(root->children_)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ctx.tree_->children_), K(ret));
} else {
if (!ctx.not_param_) { //如果该节点及其子节点的常量均不为参数, 则不需要进行替换
//对T_OP_NEG节点进行改写,解决正负数不匹配问题
//如果是T_OP_NEG->T_INT节点,则改成T_INT节点且值乘-1;
//如果是T_OP_NEG->T_DOUBLE或T_NUMBER, 则改成T_DOUBLE或T_NUMBER节点的str前加-
if (OB_ISNULL(root->children_[i])
|| root->children_[i]->is_tree_not_param_) { //如果该children在mark tree时已标记为not_param, 则不需要处理neg
//do nothing
} else if (T_OP_NEG == root->children_[i]->type_ && 1 == root->children_[i]->num_child_) {
if (OB_ISNULL(root->children_[i]->children_) || OB_ISNULL(root->children_[i]->children_[0])) {
//do nothing
} else if (T_INT != root->children_[i]->children_[0]->type_
&& T_NUMBER != root->children_[i]->children_[0]->type_
&& T_DOUBLE != root->children_[i]->children_[0]->type_
&& T_FLOAT != root->children_[i]->children_[0]->type_
&& T_VARCHAR != root->children_[i]->children_[0]->type_) {
//do nothing
} else if (T_VARCHAR == root->children_[i]->children_[0]->type_) { //T_VARCHAR不需要insert neg
root->children_[i]->children_[0]->is_neg_ = 1;
} else if ((INT64_MIN == root->children_[i]->children_[0]->value_
&& T_INT == root->children_[i]->children_[0]->type_)
|| root->children_[i]->children_[0]->is_assigned_from_child_) {
// select --9223372036854775808 from dual;
// 9223372036854775809 之前的T_OP_NEG在语法阶段就被移除,这里在转换语法树的时候不需要再插入负号
// do nothing
} else if (OB_FAIL(insert_neg_sign(*(ctx.allocator_), root->children_[i]->children_[0]))) {
SQL_PC_LOG(WARN, "fail to insert neg sign", K(ret));
} else {
root->children_[i] = root->children_[i]->children_[0];
root->children_[i]->is_neg_ = 1;
}
}
}
if (OB_SUCC(ret)) {
if (T_STMT_LIST == root->type_) {
ctx.top_node_ = root->children_[i];
}
if (T_WHERE_CLAUSE == root->type_) { //目前只处理where clause
ctx.expr_scope_ = T_WHERE_SCOPE;
}
ctx.tree_ = root->children_[i];
ctx.value_father_level_ = value_level;
ctx.assign_father_level_ = assign_level;
if (T_PROJECT_STRING == root->type_) {
if (OB_ISNULL(ctx.tree_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid child for T_PROJECT_STRING", K(ret), K(ctx.tree_));
} else if (T_VARCHAR == ctx.tree_->type_
|| (lib::is_oracle_mode() && T_CHAR == ctx.tree_->type_)) {
// 标记这一个投影列是一个常量字符串,以便后续转义字符串
ctx.tree_->is_column_varchar_ = 1;
} else {
// do nothing
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(mark_tree(ctx.tree_ , *ctx.sql_info_))) {
SQL_PC_LOG(WARN, "fail to mark function tree", K(ctx.tree_), K(ret));
}
}
if (OB_SUCC(ret)) {
//如果not_param本来就是true则不需要再判断;因为某结点判断为true,则该结点子树均为true;
if (!ctx.not_param_) {
ctx.not_param_ = is_tree_not_param(ctx.tree_);
}
}
if (OB_SUCC(ret)) {
//判断该节点是否为fast_parse 认为的常量节点
ctx.enable_contain_param_ = enable_contain_param;
if (OB_FAIL(is_fast_parse_const(ctx))) {
SQL_PC_LOG(WARN, "judge is fast parse const failed", K(ret));
}
}
if (OB_SUCC(ret)) {
ctx.ignore_scale_check_ = is_ignore_scale_check(ctx, root);
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(SMART_CALL(transform_tree(ctx, session_info)))) {
if (OB_NOT_SUPPORTED != ret) {
SQL_PC_LOG(WARN, "fail to transform tree", K(ret));
}
} else {
ctx.not_param_ = not_param;
ctx.ignore_scale_check_ = ignore_scale_check;
if (T_ALIAS == root->type_ && 0 == i) {
// alias node的param_num_处理必须等到其第一个子节点转换完之后
// select a + 1 as 'a','a'不能被参数化,但是它在raw_params数组内的下标必须是计算了1的下标之后才能得到
// alias node只有一个或者0个参数
for (int64_t param_cnt = 0; OB_SUCC(ret) && param_cnt < root->param_num_; param_cnt++) {
if (OB_FAIL(ctx.sql_info_->not_param_index_.add_member(ctx.sql_info_->total_++))) {
SQL_PC_LOG(WARN, "failed to add member", K(ctx.sql_info_->total_), K(ret));
} else if (OB_FAIL(add_varchar_charset(root, *ctx.sql_info_))) {
SQL_PC_LOG(WARN, "fail to add varchar charset", K(ret));
} else {
if (ctx.sql_info_->need_check_fp_) {
ObPCParseInfo p_info;
p_info.param_idx_ = ctx.sql_info_->total_ - 1;
p_info.flag_ = NOT_PARAM;
p_info.raw_text_pos_ = root->sql_str_off_;
if (root->sql_str_off_ == -1) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("invlid str off", K(lbt()), K(ctx.tree_),
K(root->raw_param_idx_), K(get_type_name(root->type_)),
K(session_info.get_current_query_string()),
"result_tree_", SJ(ObParserResultPrintWrapper(*ctx.top_node_)));
}
if (OB_FAIL(ret)) {
// do nithing
} else if (OB_FAIL(ctx.sql_info_->parse_infos_.push_back(p_info))) {
SQL_PC_LOG(WARN, "fail to push parser info", K(ret));
}
}
}
} // for end
} else {
// do nothing
}
}
}
}
} // for end
if (is_project_list_scope) {
ctx.is_project_list_scope_ = false;
}
}
return ret;
}
int ObSqlParameterization::check_and_generate_param_info(const ObIArray<ObPCParam *> &raw_params,
const SqlInfo &sql_info,
ObIArray<ObPCParam *> &special_params)
{
int ret = OB_SUCCESS;
if (sql_info.total_ != raw_params.count()) {
ret = OB_NOT_SUPPORTED;
if (sql_info.need_check_fp_) {
SQL_PC_LOG(ERROR, "const number of fast parse and normal parse is different",
"fast_parse_const_num", raw_params.count(),
"normal_parse_const_num", sql_info.total_);
}
}
if (OB_FAIL(ret)) {
} else {
ObPCParam *pc_param = NULL;
for (int32_t i = 0; OB_SUCC(ret) && i < raw_params.count(); i ++) {
//not param 和neg param不可能是同一个param, 在transform_tree时已保证
pc_param = raw_params.at(i);
if (OB_ISNULL(pc_param)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else if (OB_ISNULL(pc_param->node_)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(pc_param->node_), K(ret));
} else if (sql_info.not_param_index_.has_member(i)) { //not param
pc_param->flag_ = NOT_PARAM;
if (OB_FAIL(special_params.push_back(pc_param))) {
SQL_PC_LOG(WARN, "fail to push item to array", K(ret));
}
} else if (sql_info.neg_param_index_.has_member(i)) {//neg param
//如果是T_VARCHAR则不需要记录为负数, ?sql也不需要合并-?
if (T_VARCHAR == pc_param->node_->type_) {
//do nothing
} else {
pc_param->flag_ = NEG_PARAM;
if (OB_FAIL(special_params.push_back(pc_param))) {
SQL_PC_LOG(WARN, "fail to push item to array", K(ret));
}
}
} else if (sql_info.trans_from_minus_index_.has_member(i)) {
pc_param->flag_ = TRANS_NEG_PARAM;
if (OB_FAIL(special_params.push_back(pc_param))) {
SQL_PC_LOG(WARN, "failed to push back item to array", K(ret));
}
} else {
pc_param->flag_ = NORMAL_PARAM;
}
} // for end
if (sql_info.need_check_fp_) {
// do nothing
int last_pos = -1;
int last_param_idx = -1;
for (int i = 0; OB_SUCC(ret) && i < sql_info.parse_infos_.count(); i++) {
if (last_pos > sql_info.parse_infos_.at(i).raw_text_pos_ ||
last_param_idx > sql_info.parse_infos_.at(i).param_idx_) {
ret = OB_NOT_SUPPORTED;
SQL_PC_LOG(ERROR, "invalid parse order", K(last_param_idx), K(last_pos),
K(sql_info.parse_infos_.at(i).raw_text_pos_),
K(sql_info.parse_infos_.at(i).param_idx_),
K(sql_info.parse_infos_), K(ret));
} else {
last_pos = sql_info.parse_infos_.at(i).raw_text_pos_;
last_param_idx = sql_info.parse_infos_.at(i).param_idx_;
}
}
}
}
return ret;
}
//长路径sql参数化
int ObSqlParameterization::parameterize_syntax_tree(common::ObIAllocator &allocator,
bool is_transform_outline,
ObPlanCacheCtx &pc_ctx,
ParseNode *tree,
ParamStore &params,
ObCollationType cs_type)
{
int ret = OB_SUCCESS;
SqlInfo sql_info;
bool need_parameterized = false;
SQL_EXECUTION_MODE mode = get_sql_execution_mode(pc_ctx);
ObMaxConcurrentParam::FixParamStore fix_param_store(OB_MALLOC_NORMAL_BLOCK_SIZE,
ObWrapperAllocator(&allocator));
ObSQLSessionInfo *session = NULL;
ObSEArray<ObPCParam *, OB_PC_SPECIAL_PARAM_COUNT> special_params;
ObSEArray<ObString, 4> user_var_names;
int tmp_ret = OB_SUCCESS;
tmp_ret = OB_E(EventTable::EN_SQL_PARAM_FP_NP_NOT_SAME_ERROR) OB_SUCCESS;
if (OB_SUCCESS != tmp_ret) {
sql_info.need_check_fp_ = true;
}
int64_t reserved_cnt = 0;
if (OB_ISNULL(session = pc_ctx.exec_ctx_.get_my_session())) {
ret = OB_ERR_UNEXPECTED;
SQL_PC_LOG(ERROR, "got session is NULL", K(ret));
} else if (is_prepare_mode(mode)
|| is_transform_outline
) {
// if so, faster parser is needed
// otherwise, fast parser has been done before
pc_ctx.fp_result_.reset();
FPContext fp_ctx(cs_type);
fp_ctx.enable_batched_multi_stmt_ = pc_ctx.sql_ctx_.handle_batched_multi_stmt();
fp_ctx.sql_mode_ = session->get_sql_mode();
fp_ctx.is_udr_mode_ = pc_ctx.is_rewrite_sql_;
fp_ctx.def_name_ctx_ = pc_ctx.def_name_ctx_;
if (OB_FAIL(fast_parser(allocator,
fp_ctx,
pc_ctx.raw_sql_,
pc_ctx.fp_result_))) {
SQL_PC_LOG(WARN, "fail to fast parser", K(ret));
}
}
if (OB_FAIL(ret)) {
// do nothing
} else if (FALSE_IT(reserved_cnt = pc_ctx.fp_result_.raw_params_.count())) {
} else if (!is_execute_mode(mode)
&& (OB_FAIL(params.reserve(reserved_cnt)) // Reserve array to avoid extending
|| OB_FAIL(sql_info.param_charset_type_.reserve(reserved_cnt))
|| OB_FAIL(sql_info.fixed_param_idx_.reserve(reserved_cnt)))) {
LOG_WARN("failed to reserve array", K(ret));
} else if (OB_FAIL(transform_syntax_tree(allocator,
*session,
is_execute_mode(mode) ? NULL : &pc_ctx.fp_result_.raw_params_,
tree,
sql_info,
params,
is_prepare_mode(mode) ? NULL : &pc_ctx.select_item_param_infos_,
fix_param_store,
is_transform_outline,
mode,
&pc_ctx.fixed_param_info_list_))) {
if (OB_NOT_SUPPORTED != ret) {
SQL_PC_LOG(WARN, "fail to normal parameterized parser tree", K(ret));
}
} else {
need_parameterized = (!(PC_PS_MODE == pc_ctx.mode_ || PC_PL_MODE == pc_ctx.mode_)
|| (is_prepare_mode(mode) && sql_info.ps_need_parameterized_));
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(get_related_user_vars(tree, user_var_names))) {
LOG_WARN("failed to get related session vars", K(ret));
} else if (OB_FAIL(pc_ctx.sql_ctx_.set_related_user_var_names(user_var_names, allocator))) {
LOG_WARN("failed to set related user var names for sql ctx", K(ret));
} else if (is_execute_mode(mode)) {
if (OB_FAIL(gen_ps_not_param_var(sql_info.ps_not_param_offsets_, params, pc_ctx))) {
SQL_PC_LOG(WARN, "fail to gen ps not param var", K(ret));
} else if (OB_FAIL(construct_no_check_type_params(sql_info.no_check_type_offsets_,
sql_info.need_check_type_param_offsets_,
params))) {
SQL_PC_LOG(WARN, "fail to construct no check type params", K(ret));
}
} else if (need_parameterized) {
if (OB_FAIL(check_and_generate_param_info(pc_ctx.fp_result_.raw_params_,
sql_info,
special_params))) {
if (OB_NOT_SUPPORTED != ret) {
SQL_PC_LOG(WARN, "fail to check and generate param info", K(ret));
} else if (sql_info.need_check_fp_) {
SQL_PC_LOG(INFO, "print tree", K(session->get_current_query_string()), "result_tree_", SJ(ObParserResultPrintWrapper(*tree)));
} else {
// do nothing
}
} else if (OB_FAIL(gen_special_param_info(sql_info, pc_ctx))) {
SQL_PC_LOG(WARN, "fail to gen special param info", K(ret));
} else {
// do nothing
}
if (OB_SUCC(ret)) {
char *buf = NULL;
int32_t pos = 0;
buf = (char *)allocator.alloc(pc_ctx.raw_sql_.length());
if (NULL == buf) {
SQL_PC_LOG(WARN, "fail to alloc buf", K(pc_ctx.raw_sql_.length()));
ret = OB_ALLOCATE_MEMORY_FAILED;
} else if (OB_FAIL(construct_sql(pc_ctx.fp_result_.pc_key_.name_, special_params, buf, pc_ctx.raw_sql_.length(), pos))) {
SQL_PC_LOG(WARN, "fail to construct_sql", K(ret));
} else if (is_prepare_mode(mode) && OB_FAIL(transform_neg_param(pc_ctx.fp_result_.raw_params_))) {
SQL_PC_LOG(WARN, "fail to transform_neg_param", K(ret));
} else {
pc_ctx.sql_ctx_.spm_ctx_.bl_key_.constructed_sql_.assign_ptr(buf, pos);
pc_ctx.ps_need_parameterized_ = sql_info.ps_need_parameterized_;
pc_ctx.normal_parse_const_cnt_ = sql_info.total_;
}
}
}
return ret;
}
//生成not param info信息及index信息
int ObSqlParameterization::gen_special_param_info(SqlInfo &sql_info, ObPlanCacheCtx &pc_ctx)
{
int ret = OB_SUCCESS;
ParseNode *raw_param = NULL;
NotParamInfo np_info;
pc_ctx.not_param_info_.set_capacity(sql_info.not_param_index_.num_members());
for (int32_t i = 0; OB_SUCC(ret) && i < pc_ctx.fp_result_.raw_params_.count(); i ++) {
if (OB_ISNULL(pc_ctx.fp_result_.raw_params_.at(i))) {
ret = OB_INVALID_ARGUMENT;
} else if (NULL == (raw_param = pc_ctx.fp_result_.raw_params_.at(i)->node_)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else if (sql_info.not_param_index_.has_member(i)) { //not param
np_info.reset();
np_info.idx_ = i;
np_info.raw_text_ = ObString(raw_param->text_len_, raw_param->raw_text_);
if (OB_FAIL(pc_ctx.not_param_info_.push_back(np_info))) {
SQL_PC_LOG(WARN, "fail to push item to array", K(ret));
}
}
} // for end
if (OB_SUCC(ret)) {
if (OB_FAIL(pc_ctx.not_param_index_.add_members2(sql_info.not_param_index_))) {
LOG_WARN("fail to add not param index members", K(ret));
} else if (OB_FAIL(pc_ctx.neg_param_index_.add_members2(sql_info.neg_param_index_))) {
LOG_WARN("fail to add neg param index members", K(ret));
} else if (OB_FAIL(pc_ctx.neg_param_index_.add_members2(sql_info.trans_from_minus_index_))) {
LOG_WARN("failed to add trans from minus index members", K(ret));
} else if (OB_FAIL(pc_ctx.param_charset_type_.assign(sql_info.param_charset_type_))) {
LOG_WARN("fail to assign param charset type", K(ret));
} else if (OB_FAIL(pc_ctx.fixed_param_idx_.assign(sql_info.fixed_param_idx_))) {
LOG_WARN("fail to assign fixed param idx", K(ret));
} else if (OB_FAIL(pc_ctx.must_be_positive_index_.add_members2(sql_info.must_be_positive_index_))) {
LOG_WARN("failed to add bitset members", K(ret));
} else {
// do nothing
}
}
return ret;
}
int ObSqlParameterization::gen_ps_not_param_var(const ObIArray<int64_t> &offsets,
ParamStore &params,
ObPlanCacheCtx &pc_ctx)
{
int ret = OB_SUCCESS;
pc_ctx.not_param_var_.set_capacity(offsets.count());
for (int i = 0; OB_SUCC(ret) && i < offsets.count(); ++i) {
const int64_t offset = offsets.at(i);
PsNotParamInfo ps_not_param_var;
ps_not_param_var.idx_ = offset;
if (offset >= params.count()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("offset should not oversize param size", K(ret), K(offset), K(params.count()));
} else {
ps_not_param_var.ps_param_ = params.at(offset);
if (OB_FAIL(pc_ctx.not_param_var_.push_back(ps_not_param_var))) {
LOG_WARN("fail to push item to array", K(ret));
} else if (OB_FAIL(pc_ctx.not_param_index_.add_member(offset))) {
LOG_WARN("add member failed", K(ret), K(offset));
}
}
}
return ret;
}
int ObSqlParameterization::construct_no_check_type_params(const ObIArray<int64_t> &no_check_type_offsets,
const ObBitSet<> &need_check_type_offsets,
ParamStore &params)
{
int ret = OB_SUCCESS;
for (int64_t i = 0; OB_SUCC(ret) && i < no_check_type_offsets.count(); i++) {
const int64_t offset = no_check_type_offsets.at(i);
if (offset < 0 || offset >= params.count()) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("Invalid offset", K(ret), K(offset), K(params.count()));
} else if (need_check_type_offsets.has_member(offset)) {
// do nothing
} else if (!params.at(offset).is_ext()) { // extend type need to be checked
params.at(offset).set_need_to_check_type(false);
} else {
// real type do not need to be checked
params.at(offset).set_need_to_check_extend_type(false);
}
} // for end
LOG_DEBUG("ps obj param infos", K(params), K(no_check_type_offsets), K(need_check_type_offsets));
return ret;
}
int ObSqlParameterization::transform_neg_param(ObIArray<ObPCParam *> &pc_params)
{
int ret = OB_SUCCESS;
ObPCParam *pc_param = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < pc_params.count(); i ++) {
pc_param = pc_params.at(i);
if (OB_ISNULL(pc_param)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else if (TRANS_NEG_PARAM == pc_param->flag_) {
int64_t tmp_pos = 0;
for (; tmp_pos < pc_param->node_->str_len_ && isspace(pc_param->node_->str_value_[tmp_pos]); tmp_pos++);
if (OB_UNLIKELY(tmp_pos >= pc_param->node_->str_len_)) {
int ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(tmp_pos), K(pc_param->node_->str_len_), K(ret));
} else {
if ('-' != pc_param->node_->str_value_[tmp_pos]) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expected neg sign here", K(tmp_pos), K(ObString(pc_param->node_->str_len_,
pc_param->node_->str_value_)));
} else {
tmp_pos += 1;
for (; tmp_pos < pc_param->node_->str_len_ && isspace(pc_param->node_->str_value_[tmp_pos]); tmp_pos++);
}
if (OB_SUCC(ret)) {
pc_param->node_->str_value_ += tmp_pos;
pc_param->node_->str_len_ -= tmp_pos;
if (T_NUMBER != pc_param->node_->type_ && pc_param->node_->value_ < 0) {
pc_param->node_->value_ = 0 - pc_param->node_->value_;
}
}
}
}
}
return ret;
}
int ObSqlParameterization::construct_not_param(const ObString &no_param_sql,
ObPCParam *pc_param,
char *buf,
int32_t buf_len,
int32_t &pos,
int32_t &idx)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(pc_param)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else {
int32_t len = (int32_t)pc_param->node_->pos_ - idx;
if (len > buf_len - pos) {
ret = OB_BUF_NOT_ENOUGH;
} else if (len > 0) {
//copy text
MEMCPY(buf + pos, no_param_sql.ptr() + idx, len);
idx = (int32_t)pc_param->node_->pos_ + 1;
pos += len;
//copy raw param
MEMCPY(buf + pos, pc_param->node_->raw_text_, pc_param->node_->text_len_);
pos += (int32_t)pc_param->node_->text_len_;
}
}
return ret;
}
int ObSqlParameterization::construct_neg_param(const ObString &no_param_sql,
ObPCParam *pc_param,
char *buf,
int32_t buf_len,
int32_t &pos,
int32_t &idx)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(pc_param)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else {
int32_t len = (int32_t)pc_param->node_->pos_ - idx;
if (len > buf_len - pos) {
ret = OB_BUF_NOT_ENOUGH;
} else if (len > 0) {
MEMCPY(buf + pos, no_param_sql.ptr() + idx, len);
pos += len;
}
if (OB_SUCC(ret)) {
idx = (int32_t)pc_param->node_->pos_ + 1;
buf[pos++] = '?';
}
}
return ret;
}
int ObSqlParameterization::construct_trans_neg_param(const ObString &no_param_sql,
ObPCParam *pc_param,
char *buf,
int32_t buf_len,
int32_t &pos,
int32_t &idx)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(pc_param)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else {
int32_t len = (int32_t)pc_param->node_->pos_ - idx;
if (len > buf_len - pos) {
ret = OB_BUF_NOT_ENOUGH;
} else if (len > 0) {
MEMCPY(buf + pos, no_param_sql.ptr() + idx, len);
pos += len;
}
if (OB_SUCC(ret)) {
// for 'select * from t where a - 1 = 2', the statement is 'select * from t where a - ? = ?'
// so we need fill spaces between '-' and '?', so here it is
buf[pos++] = '-';
int64_t tmp_pos = 0;
for (; tmp_pos < pc_param->node_->str_len_ && isspace(pc_param->node_->str_value_[tmp_pos]); tmp_pos++);
if (OB_UNLIKELY(tmp_pos >= pc_param->node_->str_len_)) {
int ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(tmp_pos), K(pc_param->node_->str_len_), K(ret));
} else {
if ('-' != pc_param->node_->str_value_[tmp_pos]) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("expected neg sign here", K(tmp_pos), K(ObString(pc_param->node_->str_len_,
pc_param->node_->str_value_)));
} else {
tmp_pos += 1;
for (; tmp_pos < pc_param->node_->str_len_ && isspace(pc_param->node_->str_value_[tmp_pos]); tmp_pos++) {
buf[pos++] = ' ';
}
}
if (OB_SUCC(ret)) {
buf[pos++] = '?';
idx = pc_param->node_->pos_ + 1;
}
}
}
}
return ret;
}
int ObSqlParameterization::construct_sql(const ObString &no_param_sql,
ObIArray<ObPCParam *> &pc_params,
char *buf,
int32_t buf_len,
int32_t &pos) //已存的长度
{
int ret = OB_SUCCESS;
int32_t idx = 0; //原始带?sql的偏移位置
ObPCParam *pc_param = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < pc_params.count(); i ++) {
pc_param = pc_params.at(i);
int32_t len = 0; //需要copy的text的长度
if (OB_ISNULL(pc_param)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else if (NOT_PARAM == pc_param->flag_) {
OZ (construct_not_param(no_param_sql, pc_param, buf, buf_len, pos, idx));
} else if (NEG_PARAM == pc_param->flag_) {
OZ (construct_neg_param(no_param_sql, pc_param, buf, buf_len, pos, idx));
} else if (TRANS_NEG_PARAM == pc_param->flag_) {
OZ (construct_trans_neg_param(no_param_sql, pc_param, buf, buf_len, pos, idx));
} else {
//do nothing
}
} //for end
if (OB_SUCCESS == ret) {
int32_t len = no_param_sql.length() - idx;
if (len > buf_len - pos) {
ret = OB_BUF_NOT_ENOUGH;
} else if (len > 0) {
MEMCPY(buf + pos, no_param_sql.ptr() + idx, len);
idx += len;
pos += len;
}
}
return ret;
}
int ObSqlParameterization::construct_sql_for_pl(const ObString &no_param_sql,
ObIArray<ObPCParam *> &pc_params,
char *buf,
int32_t buf_len,
int32_t &pos) //已存的长度
{
int ret = OB_SUCCESS;
int32_t idx = 0; //原始带?sql的偏移位置
ObPCParam *pc_param = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < pc_params.count(); i ++) {
pc_param = pc_params.at(i);
int32_t len = 0; //需要copy的text的长度
if (OB_ISNULL(pc_param)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else if (NOT_PARAM == pc_param->flag_) {
int32_t len = (int32_t)pc_param->node_->pos_ - idx;
if (0 == len) {
MEMCPY(buf + pos, pc_param->node_->raw_text_, pc_param->node_->text_len_);
pos += (int32_t)pc_param->node_->text_len_;
idx = (int32_t)pc_param->node_->pos_ + 1;
} else {
OZ (construct_not_param(no_param_sql, pc_param, buf, buf_len, pos, idx));
}
} else if (NEG_PARAM == pc_param->flag_) {
OZ (construct_neg_param(no_param_sql, pc_param, buf, buf_len, pos, idx));
} else if (TRANS_NEG_PARAM == pc_param->flag_) {
OZ (construct_trans_neg_param(no_param_sql, pc_param, buf, buf_len, pos, idx));
} else {
//do nothing
}
} //for end
if (OB_SUCCESS == ret) {
int32_t len = no_param_sql.length() - idx;
if (len > buf_len - pos) {
ret = OB_BUF_NOT_ENOUGH;
} else if (len > 0) {
MEMCPY(buf + pos, no_param_sql.ptr() + idx, len);
idx += len;
pos += len;
}
}
return ret;
}
bool ObSqlParameterization::need_fast_parser(const ObString &sql)
{
bool b_ret = true;
const char *stmt = sql.ptr();
int64_t len = sql.length();
int64_t leading_space_len = 0;
while (leading_space_len < len && isspace(stmt[leading_space_len])) {
++leading_space_len;
}
len -= leading_space_len;
stmt += leading_space_len;
if (len > 4 && ('s' == stmt[0] || 'S' == stmt[0]) && ('h' == stmt[1] || 'H' == stmt[1])) {
if (0 == STRNCASECMP(stmt, "show", 4)) {
b_ret = false;
}
}
return b_ret;
}
int ObSqlParameterization::fast_parser(ObIAllocator &allocator,
const FPContext &fp_ctx,
const ObString &sql,
ObFastParserResult &fp_result)
{
//UNUSED(sql_mode);
int ret = OB_SUCCESS;
int64_t param_num = 0;
char *no_param_sql_ptr = NULL;
int64_t no_param_sql_len = 0;
ParamList *p_list = NULL;
bool is_call_procedure = false;
bool is_contain_select = (sql.length() > 6 && 0 == STRNCASECMP(sql.ptr(), "select", 6));
if (!is_contain_select && (!need_fast_parser(sql)
|| (ObParser::is_pl_stmt(sql, nullptr, &is_call_procedure) && !is_call_procedure))) {
(void)fp_result.pc_key_.name_.assign_ptr(sql.ptr(), sql.length());
} else if (GCONF._ob_enable_fast_parser) {
if (OB_FAIL(ObFastParser::parse(sql, fp_ctx, allocator, no_param_sql_ptr,
no_param_sql_len, p_list, param_num, fp_result.question_mark_ctx_))) {
LOG_WARN("fast parse error", K(param_num),
K(ObString(no_param_sql_len, no_param_sql_ptr)), K(sql));
}
if (OB_SUCC(ret)) {
(void)fp_result.pc_key_.name_.assign_ptr(no_param_sql_ptr, no_param_sql_len);
if (param_num > 0) {
ObPCParam *pc_param = NULL;
char *ptr = (char *)allocator.alloc(param_num * sizeof(ObPCParam));
fp_result.raw_params_.reset();
fp_result.raw_params_.set_allocator(&allocator);
fp_result.raw_params_.set_capacity(param_num);
if (OB_ISNULL(ptr)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
SQL_PC_LOG(WARN, "fail to alloc memory for pc param", K(ret), K(ptr));
}
for (int64_t i = 0; OB_SUCC(ret) && i < param_num && NULL != p_list; i++) {
pc_param = new(ptr)ObPCParam();
ptr += sizeof(ObPCParam);
pc_param->node_ = p_list->node_;
if (OB_FAIL(fp_result.raw_params_.push_back(pc_param))) {
SQL_PC_LOG(WARN, "fail to push into params", K(ret));
} else {
p_list = p_list->next_;
}
} // for end
} else { /*do nothing*/}
}
} else {
ObParser parser(allocator, fp_ctx.sql_mode_, fp_ctx.conn_coll_);
SMART_VAR(ParseResult, parse_result) {
if (OB_FAIL(parser.parse(sql, parse_result, FP_MODE, fp_ctx.enable_batched_multi_stmt_))) {
SQL_PC_LOG(WARN, "fail to fast parser", K(sql), K(ret));
} else {
(void)fp_result.pc_key_.name_.assign_ptr(parse_result.no_param_sql_, parse_result.no_param_sql_len_);
int64_t param_num = parse_result.param_node_num_;
//copy raw params
if (param_num > 0) {
ObPCParam *pc_param = NULL;
ParamList *p_list = parse_result.param_nodes_;
char *ptr = (char *)allocator.alloc(param_num * sizeof(ObPCParam));
fp_result.raw_params_.set_allocator(&allocator);
fp_result.raw_params_.set_capacity(param_num);
if (OB_ISNULL(ptr)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
SQL_PC_LOG(WARN, "fail to alloc memory for pc param", K(ret), K(ptr));
}
for (int64_t i = 0;
OB_SUCC(ret) && i < param_num && NULL != p_list;//当p_list = NULL,表示链表结束
i++) {
pc_param = new(ptr)ObPCParam();
ptr += sizeof(ObPCParam);
pc_param->node_ = p_list->node_;
if (OB_FAIL(fp_result.raw_params_.push_back(pc_param))) {
SQL_PC_LOG(WARN, "fail to push into params", K(ret));
} else {
p_list = p_list->next_;
}
} // for end
} else { /*do nothing*/}
}
}
}
return ret;
}
//used for outline
int ObSqlParameterization::raw_fast_parameterize_sql(ObIAllocator &allocator,
const ObSQLSessionInfo &session,
const ObString &sql,
ObString &no_param_sql,
ObIArray<ObPCParam *> &raw_params,
ParseMode parse_mode)
{
int ret = OB_SUCCESS;
ObParser parser(allocator, session.get_sql_mode(), session.get_local_collation_connection());
ParseResult parse_result;
NG_TRACE(pc_fast_parse_start);
if (OB_FAIL(parser.parse(sql,
parse_result,
parse_mode,
false))) {
SQL_PC_LOG(WARN, "fail to parse query", K(ret));
}
NG_TRACE(pc_fast_parse_end);
if (OB_SUCC(ret)) {
no_param_sql.assign(parse_result.no_param_sql_, parse_result.no_param_sql_len_);
}
if (OB_SUCC(ret)) {
ParamList *param = parse_result.param_nodes_;
for (int32_t i = 0;
OB_SUCC(ret) && i < parse_result.param_node_num_ && NULL != param;//当param = NULL,表示链表结束
i ++) {
void *ptr = allocator.alloc(sizeof(ObPCParam));
if (OB_ISNULL(ptr)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
SQL_PC_LOG(ERROR, "fail to alloc memory for pc param", K(ret), K(ptr));
} else {
ObPCParam *pc_param = new(ptr)ObPCParam();
pc_param->node_ = param->node_;
if (OB_FAIL(raw_params.push_back(pc_param))) {
SQL_PC_LOG(WARN, "fail to push into params", K(ret));
} else {
param = param->next_;
}
}
} // for end
}
SQL_PC_LOG(DEBUG, "after raw fp", K(parse_result.param_node_num_));
return ret;
}
int ObSqlParameterization::insert_neg_sign(ObIAllocator &alloc_buf, ParseNode *node)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(node)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(node), K(ret));
} else if (T_INT != node->type_ && T_DOUBLE != node->type_ && T_FLOAT != node->type_ && T_NUMBER != node->type_) {
ret = OB_ERR_UNEXPECTED;
SQL_PC_LOG(WARN, "invalid neg digit type", K(node->type_), K(ret));
} else {
if (T_INT == node->type_) {
node->value_ = -node->value_;
}
char *new_str = static_cast<char *>(parse_malloc(node->str_len_ + 2, &alloc_buf));
char *new_raw_text = static_cast<char *>(parse_malloc(node->text_len_ + 2, &alloc_buf));
if (OB_ISNULL(new_str) || OB_ISNULL(new_raw_text)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
SQL_PC_LOG(ERROR, "parse_strdup failed", K(ret), K(new_raw_text), K(new_str));
} else {
new_str[0] = '-';
new_raw_text[0] = '-';
MEMMOVE(new_str+1, node->str_value_, node->str_len_);
MEMMOVE(new_raw_text+1, node->raw_text_, node->text_len_);
new_str[node->str_len_ + 1] = '\0';
new_raw_text[node->text_len_ + 1] = '\0';
node->str_value_ = new_str;
node->raw_text_ = new_raw_text;
node->str_len_++;
node->text_len_++;
}
}
return ret;
}
int ObSqlParameterization::add_param_flag(const ParseNode *node, SqlInfo &sql_info)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(node)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else if (1 == node->is_neg_) {
if (OB_FAIL(sql_info.neg_param_index_.add_member(sql_info.total_))) {
SQL_PC_LOG(WARN, "failed to add neg param index", K(sql_info.total_), K(ret));
}
} else if (node->is_trans_from_minus_) {
if (OB_FAIL(sql_info.trans_from_minus_index_.add_member(sql_info.total_))) {
SQL_PC_LOG(WARN, "failed to add trans_from_minus index", K(sql_info.total_), K(ret));
}
} else {
// do nothing
}
return ret;
}
int ObSqlParameterization::add_not_param_flag(const ParseNode *node, SqlInfo &sql_info)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(node)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret));
} else if (T_QUESTIONMARK == node->type_) {
if (OB_FAIL(sql_info.ps_not_param_offsets_.push_back(node->value_))) {
LOG_WARN("pushback offset failed", K(node->value_));
} else if (OB_FAIL(sql_info.not_param_index_.add_member(node->value_))) {
SQL_PC_LOG(WARN, "failed to add member", K(node->value_));
}
} else if (T_CAST_ARGUMENT == node->type_ //如果是cast类型,则需要添加N个cast节点对应的常数, 因为正常parse不识别为常量, 但fast parse时会识别为常量
|| T_COLLATION == node->type_
|| T_NULLX_CLAUSE == node->type_ // deal null clause on json expr
|| T_WEIGHT_STRING_LEVEL_PARAM == node->type_) {
for (int i = 0; OB_SUCC(ret) && i < node->param_num_; ++i) {
if (OB_FAIL(sql_info.not_param_index_.add_member(sql_info.total_++))) {
SQL_PC_LOG(WARN, "failed to add member", K(sql_info.total_));
} else if (OB_FAIL(add_varchar_charset(node, sql_info))) {
SQL_PC_LOG(WARN, "fail to add varchar charset", K(ret));
}
if (sql_info.need_check_fp_) {
ObPCParseInfo p_info;
p_info.param_idx_ = sql_info.total_ - 1;
p_info.flag_ = NOT_PARAM;
p_info.raw_text_pos_ = node->sql_str_off_;
if (node->sql_str_off_ == -1) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("invlid str off", K(lbt()), K(node),
K(node->raw_param_idx_), K(get_type_name(node->type_)));
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(sql_info.parse_infos_.push_back(p_info))) {
SQL_PC_LOG(WARN, "fail to push parser info", K(ret));
}
}
}
} else {
if (OB_FAIL(sql_info.not_param_index_.add_member(sql_info.total_++))) {
SQL_PC_LOG(WARN, "failed to add member", K(sql_info.total_));
} else if (OB_FAIL(add_varchar_charset(node, sql_info))) {
SQL_PC_LOG(WARN, "fail to add varchar charset", K(ret));
}
if (sql_info.need_check_fp_) {
ObPCParseInfo p_info;
p_info.param_idx_ = sql_info.total_ - 1;
p_info.flag_ = NOT_PARAM;
p_info.raw_text_pos_ = node->sql_str_off_;
if (node->sql_str_off_ == -1) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("invlid str off", K(lbt()), K(node),
K(node->raw_param_idx_), K(get_type_name(node->type_)));
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(sql_info.parse_infos_.push_back(p_info))) {
SQL_PC_LOG(WARN, "fail to push parser info", K(ret));
}
}
}
return ret;
}
//T_FUN_SYS类型函数
//根据mark_arr将func中参数节点标记为该节点及其子节点不能参数化
int ObSqlParameterization::mark_args(ParseNode *arg_tree,
const bool *mark_arr,
int64_t arg_num,
SqlInfo &sql_info)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(arg_tree) || OB_ISNULL(mark_arr)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret), K(arg_tree));
} else if (OB_ISNULL(arg_tree->children_)
|| arg_num != arg_tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret),
K(arg_tree->type_), K(arg_tree->children_), K(arg_tree->num_child_));
} else {
sql_info.ps_need_parameterized_ = false;
for (int64_t i = 0; OB_SUCC(ret) && i < arg_num; i++) {
if (true == mark_arr[i]) {
if (OB_ISNULL(arg_tree->children_[i])) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret), K(arg_tree->children_[i]));
} else {
arg_tree->children_[i]->is_tree_not_param_ = true;
}
}
}
}
return ret;
}
// 对于不能参数化需要特殊标记的节点进行标记, 一般是不能直接通过节点类型判别的节点可在该函数中进行标记
// Mark those special nodes that cannot be parameterized.
// After mark this node, it has following mechanism:
// If a node is marked as cannot be parameterized,
// CUREENT NODE AND ALL NODES OF IT'S SUBTREE cannot be parameterized.
int ObSqlParameterization::mark_tree(ParseNode *tree ,SqlInfo &sql_info)
{
int ret = OB_SUCCESS;
if (NULL == tree) {
//do nothing
} else if (T_FUN_SYS == tree->type_) {
ParseNode **node = tree->children_;//node[0] : func name, node[1]: arg list
if (2 != tree->num_child_) {
//do nothing 如果不是fun_name和arg_list则不在需要标记的考虑内
} else if (OB_ISNULL(node) || OB_ISNULL(node[0]) || OB_ISNULL(node[1])) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret), K(node));
} else {
ObString func_name(node[0]->str_len_, node[0]->str_value_);
if ((0 == func_name.case_compare("USERENV")
|| 0 == func_name.case_compare("UNIX_TIMESTAMP"))
&& (1 == node[1]->num_child_)) {
// USERENV函数的返回类型是由参数的具体值来决定,如果参数化后,无法拿到具体值,所以暂时不进行参数化
// UNIX_TIMESTAMP(param_str),结果的精度和param_str有关,不能参数化
const int64_t ARGS_NUMBER_ONE = 1;
bool mark_arr[ARGS_NUMBER_ONE] = {1}; //0表示参数化, 1 表示不参数化
if (OB_FAIL(mark_args(node[1], mark_arr, ARGS_NUMBER_ONE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark arg", K(ret));
}
} else if ((0 == func_name.case_compare("substr")
|| 0 == func_name.case_compare("extract_xml"))
&& (3 == node[1]->num_child_)) {
const int64_t ARGS_NUMBER_THREE = 3;
bool mark_arr[ARGS_NUMBER_THREE] = {0, 1, 1}; //0表示参数化, 1 表示不参数化
if (OB_FAIL(mark_args(node[1], mark_arr, ARGS_NUMBER_THREE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark arg", K(ret));
}
} else if (0 == func_name.case_compare("xmlserialize")
&& (10 == node[1]->num_child_)) {
const int64_t ARGS_NUMBER_TEN = 10;
bool mark_arr[ARGS_NUMBER_TEN] = {1, 0, 1, 1, 1, 1, 1, 1, 1, 1}; //0表示参数化, 1 表示不参数化
if (OB_FAIL(mark_args(node[1], mark_arr, ARGS_NUMBER_TEN, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark weight_string arg", K(ret));
}
}else if (0 == func_name.case_compare("weight_string")
&& (5 == node[1]->num_child_)) {
const int64_t ARGS_NUMBER_FIVE = 5;
bool mark_arr[ARGS_NUMBER_FIVE] = {0, 1, 1, 1, 1}; //0表示参数化, 1 表示不参数化
if (OB_FAIL(mark_args(node[1], mark_arr, ARGS_NUMBER_FIVE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark weight_string arg", K(ret));
}
} else if ((0==func_name.case_compare("convert")
|| (0==func_name.case_compare("char")))
&& (2 == node[1]->num_child_)) {
const int64_t ARGS_NUMBER_TWO = 2;
bool mark_arr[ARGS_NUMBER_TWO] = {0, 1};
if (OB_FAIL(mark_args(node[1], mark_arr, ARGS_NUMBER_TWO, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
} else if ((0 == func_name.case_compare("str_to_date") // STR_TO_DATE(str,format)
|| 0 == func_name.case_compare("date_format") //DATE_FORMAT(date,format)
|| 0 == func_name.case_compare("from_unixtime")//FROM_UNIXTIME(unix_timestamp), FROM_UNIXTIME(unix_timestamp,format)
|| 0 == func_name.case_compare("round") //ROUND(X), ROUND(X,D)
|| 0 == func_name.case_compare("left") // the length of result should be set with the value of the second param
|| 0 == func_name.case_compare("substr")
|| 0 == func_name.case_compare("dbms_lob_convert_clob_charset")
|| 0 == func_name.case_compare("truncate")) // truncate结果的精度需要根据第二个参数进行推导,所以不能参数化
&& (2 == node[1]->num_child_)) {
const int64_t ARGS_NUMBER_TWO = 2;
bool mark_arr[ARGS_NUMBER_TWO] = {0, 1};
if (OB_FAIL(mark_args(node[1], mark_arr, ARGS_NUMBER_TWO, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark arg", K(ret));
}
} else if ((0 == func_name.case_compare("name_const"))
&& (2 == node[1]->num_child_)) {
const int64_t ARGS_NUMBER_TWO = 2;
bool mark_arr[ARGS_NUMBER_TWO] = {1, 0};
if (OB_FAIL(mark_args(node[1], mark_arr, ARGS_NUMBER_TWO, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark arg", K(ret));
}
} else if ((0 == func_name.case_compare("concat")) && 1 == node[0]->reserved_) {
sql_info.ps_need_parameterized_ = false;
}
}
} else if (T_OP_LIKE == tree->type_) {
if (3 == tree->num_child_) { // child[0] like child[1] escape child[2]
const int64_t ARGS_NUMBER_THREE = 3;
bool mark_arr[ARGS_NUMBER_THREE] = {0, 1, 1}; //0表示参数化, 1 表示不参数化
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_THREE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
}
} else if (T_OP_IS == tree->type_ || T_OP_IS_NOT == tree->type_) {
if (tree->num_child_ == 2) {
const int64_t ARGS_NUMBER_TWO = 2;
bool mark_arr[ARGS_NUMBER_TWO] = {0,1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_TWO, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
} else { /*do nothing*/ }
} else if(T_FUN_SYS_JSON_VALUE == tree->type_) {
if (9 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid json value expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_NINE = 9;
bool mark_arr[ARGS_NUMBER_NINE] = {0, 1, 1, 1, 1, 1, 1, 1, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_NINE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
}
} else if(T_FUN_SYS_JSON_OBJECT == tree->type_) {
if (5 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid json object expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_FIVE = 5;
bool mark_arr[ARGS_NUMBER_FIVE] = {1, 1, 1, 1, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_FIVE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
}
} else if(T_FUN_SYS_IS_JSON == tree->type_) {
if (5 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument num for IS json", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_FIVE = 5;
bool mark_arr[ARGS_NUMBER_FIVE] = {0, 1, 1, 1, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_FIVE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
}
} else if(T_FUN_SYS_JSON_QUERY == tree->type_) {
if (10 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid json query expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_TEN = 10;
bool mark_arr[ARGS_NUMBER_TEN] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; // json doc type will affect returning type,
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_TEN, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
}
} else if(T_FUN_SYS_JSON_EXISTS == tree->type_) {
if (5 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument num for json_exists", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_FIVE = 5;
bool mark_arr[ARGS_NUMBER_FIVE] = {0, 0, 1, 1, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_FIVE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark json_exists arg", K(ret));
}
}
} else if(T_FUN_SYS_JSON_EQUAL == tree->type_) {
if (3 < tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid json query expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_THREE = 3;
bool mark_arr[ARGS_NUMBER_THREE] = {0, 0, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_THREE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark substr arg", K(ret));
}
}
} else if(T_FUN_SYS_JSON_ARRAY == tree->type_) {
if (4 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid json array expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_FOUR = 4;
bool mark_arr[ARGS_NUMBER_FOUR] = {0, 1, 1, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_FOUR, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark json array arg", K(ret));
}
}
} else if(T_FUN_SYS_JSON_MERGE_PATCH == tree->type_) {
if (7 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid json mergepatch expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_SEVEN = 7;
bool mark_arr[ARGS_NUMBER_SEVEN] = {0, 0, 1, 1, 1, 1, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_SEVEN, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark json mergepatch arg", K(ret));
}
}
} else if (T_JSON_TABLE_EXPRESSION == tree->type_) {
if (5 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid json mergepatch expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_FIVE = 5;
bool mark_arr[ARGS_NUMBER_FIVE] = {0, 1, 1, 1, 1};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_FIVE, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark json mergepatch arg", K(ret));
}
}
} else if (T_FUN_SYS_TREAT == tree->type_) {
if (2 != tree->num_child_) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid treat expr argument", K(ret), K(tree->num_child_));
} else {
const int64_t ARGS_NUMBER_TWO = 2;
bool mark_arr[ARGS_NUMBER_TWO] = {1, 0};
if (OB_FAIL(mark_args(tree, mark_arr, ARGS_NUMBER_TWO, sql_info))) {
SQL_PC_LOG(WARN, "fail to mark treat arg", K(ret));
}
}
} else { /*do nothing*/ }
return ret;
}
int ObSqlParameterization::add_varchar_charset(const ParseNode *node, SqlInfo &sql_info)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(node)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(node));
} else if (T_VARCHAR == node->type_
&& NULL != node->children_
&& NULL != node->children_[0]
&& T_CHARSET == node->children_[0]->type_) {
ObString charset(node->children_[0]->str_len_,
node->children_[0]->str_value_);
ObCharsetType charset_type = CHARSET_INVALID;
if (CHARSET_INVALID == (charset_type =
ObCharset::charset_type(charset.trim()))) {
ret = OB_ERR_UNKNOWN_CHARSET;
LOG_USER_ERROR(OB_ERR_UNKNOWN_CHARSET, charset.length(), charset.ptr());
} else if (OB_FAIL(sql_info.param_charset_type_.push_back(charset_type))) {
SQL_PC_LOG(WARN, "fail to add charset type", K(ret));
}
} else if (OB_FAIL(sql_info.param_charset_type_.push_back(CHARSET_INVALID))) {
SQL_PC_LOG(WARN, "fail to add charset type", K(ret));
}
return ret;
}
int ObSqlParameterization::get_related_user_vars(const ParseNode *tree, common::ObIArray<common::ObString> &user_vars)
{
int ret = OB_SUCCESS;
ObString var_str;
if (tree == NULL) {
// do nothing
} else {
if (T_USER_VARIABLE_IDENTIFIER == tree -> type_) {
if (OB_ISNULL(tree -> str_value_) || tree -> str_len_ <= 0) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(tree -> str_value_), K(tree -> str_len_));
} else {
var_str.assign_ptr(tree -> str_value_, static_cast<int32_t>(tree -> str_len_));
if (OB_FAIL(user_vars.push_back(var_str))) {
LOG_WARN("failed to push back user variable", K(ret));
}
}
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < tree -> num_child_; i++) {
if (OB_ISNULL(tree -> children_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(tree -> children_), K(ret));
} else if (OB_FAIL(SMART_CALL(get_related_user_vars(tree -> children_[i], user_vars)))) {
LOG_WARN("failed to get related user vars", K(ret), K(tree -> children_[i]), K(i));
}
}
}
}
if (OB_FAIL(ret)) {
user_vars.reset();
}
return ret;
}
int ObSqlParameterization::get_select_item_param_info(const common::ObIArray<ObPCParam *> &raw_params,
ParseNode *tree,
SelectItemParamInfoArray *select_item_param_infos)
{
int ret = OB_SUCCESS;
SelectItemParamInfo param_info;
ObString org_field_name;
int64_t expr_pos = tree->raw_sql_offset_;
int64_t buf_len = SelectItemParamInfo::PARAMED_FIELD_BUF_LEN;
ObSEArray<TraverseStackFrame, 64> stack_frames;
if (T_PROJECT_STRING != tree->type_ || OB_ISNULL(tree->children_) || tree->num_child_ <= 0) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(tree->type_), K(tree->children_), K(tree->num_child_));
} else if (T_ALIAS == tree->children_[0]->type_
|| T_STAR == tree->children_[0]->type_) { // have alias name, or is a '*', do not need parameterized
// do nothing
} else if (OB_FAIL(stack_frames.push_back(TraverseStackFrame{tree, 0}))) {
LOG_WARN("failed to push back element", K(ret));
} else {
// start to construct paramed field name template...
if (lib::is_oracle_mode()) { // oracle要使用原始的字符串,以正确处理偏移
org_field_name.assign_ptr(tree->raw_text_, (int32_t)tree->text_len_);
} else {
org_field_name.assign_ptr(tree->str_value_, (int32_t)tree->str_len_);
}
SelectItemTraverseCtx ctx(raw_params,
tree,
org_field_name,
tree->raw_sql_offset_,
buf_len,
expr_pos,
param_info);
// 模拟函数递归操作,遍历子树
// 栈上每一个元素为 {cur_node, next_child_idx}
// 出栈操作:
// 如cur_node是的is_val_paramed_item_idx_为true,说明这是一个T_PROJECT_STRING,并且已经被遍历
// 或者T_QUESTION_MARK,或子节点都已经遍历完,出栈
//
// 入栈操作:
// 选取cur_node的第一个不为空的子节点push到堆栈中,并更新当前堆栈的next_child_idx_
for (; OB_SUCC(ret) && stack_frames.count() > 0; ) {
int64_t frame_idx = stack_frames.count() - 1;
ctx.tree_ = stack_frames.at(frame_idx).cur_node_;
if (NULL == ctx.tree_) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid null node", K(ret), K(ctx.tree_));
} else if (1 == ctx.tree_->is_val_paramed_item_idx_
|| T_QUESTIONMARK == ctx.tree_->type_
|| stack_frames.at(frame_idx).next_child_idx_ >= ctx.tree_->num_child_) {
if (T_QUESTIONMARK == ctx.tree_->type_) {
if (ctx.param_info_.name_len_ >= ctx.buf_len_) {
// column长度已经满了,不需要再继续构造模板,直接跳出
break;
} else if (OB_FAIL(resolve_paramed_const(ctx))) {
LOG_WARN("failed to resolve paramed const", K(ret));
} else {
// do nothing
}
}
// pop stack
stack_frames.pop_back();
--frame_idx;
LOG_DEBUG("after popping frame", K(stack_frames), K(frame_idx));
} else {
// do nothing
}
if (OB_FAIL(ret) || frame_idx < 0) {
// do nothing
} else if (stack_frames.at(frame_idx).cur_node_->num_child_ > 0) {
if (OB_ISNULL(stack_frames.at(frame_idx).cur_node_->children_)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid null children", K(ret), K(stack_frames.at(frame_idx).cur_node_->children_), K(frame_idx));
} else {
TraverseStackFrame frame = stack_frames.at(frame_idx);
for (int64_t i = frame.next_child_idx_; OB_SUCC(ret) && i < frame.cur_node_->num_child_; i++) {
if (OB_ISNULL(frame.cur_node_->children_[i])) {
stack_frames.at(frame_idx).next_child_idx_ = i + 1;
} else if (OB_FAIL(stack_frames.push_back(TraverseStackFrame{frame.cur_node_->children_[i], 0}))) {
LOG_WARN("failed to push back eleemnt", K(ret));
} else {
stack_frames.at(frame_idx).next_child_idx_ = i + 1;
LOG_DEBUG("after pushing frame", K(stack_frames));
break;
}
} // for end
}
}
} // for end
if (OB_SUCC(ret)) {
// 如果常量之后还有字符串
int64_t res_start_pos = expr_pos - tree->raw_sql_offset_;
int64_t tmp_len = std::min(org_field_name.length() - res_start_pos, buf_len - param_info.name_len_);
if (tmp_len > 0) {
int32_t len = static_cast<int32_t>(tmp_len);
MEMCPY(param_info.paramed_field_name_ + param_info.name_len_, org_field_name.ptr() + res_start_pos, len);
param_info.name_len_ += len;
}
if (T_QUESTIONMARK == tree->children_[0]->type_
&& 1 == tree->children_[0]->is_column_varchar_) {
param_info.esc_str_flag_ = true;
}
}
}
if (OB_FAIL(ret) || 0 == param_info.params_idx_.count()) {
// do nothing
} else if (OB_FAIL(select_item_param_infos->push_back(param_info))) {
SQL_PC_LOG(WARN, "failed to push back element", K(ret));
} else {
tree->value_ = select_item_param_infos->count() - 1;
tree->is_val_paramed_item_idx_ = 1;
LOG_DEBUG("add a paramed info", K(param_info));
}
return ret;
}
int ObSqlParameterization::resolve_paramed_const(SelectItemTraverseCtx &ctx)
{
int ret = OB_SUCCESS;
int64_t idx = ctx.tree_->raw_param_idx_;
if (idx >= ctx.raw_params_.count()) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid argument", K(ret), K(idx), K(ctx.raw_params_.count()));
} else if (OB_ISNULL(ctx.raw_params_.at(idx)) || OB_ISNULL(ctx.raw_params_.at(idx)->node_)) {
ret = OB_INVALID_ARGUMENT;
SQL_PC_LOG(WARN, "invalid arguemnt", K(ret));
} else {
const ParseNode *param_node = ctx.raw_params_.at(idx)->node_;
int64_t tmp_len = std::min(ctx.buf_len_ - ctx.param_info_.name_len_, param_node->raw_sql_offset_ - ctx.expr_pos_);
// In the case of select _binary 'abc';, special processing is required, because the value of
// org_expr_name_ is the same as that of raw param in this scenario.
// So it is judged here that if org_expr_name_ is the same as param_node->str_value_ value
// there is no need to copy it. paramed_field_name_ should be replaced with '?'
if (0 == ctx.org_expr_name_.case_compare(ObString(param_node->str_len_, param_node->str_value_))) {
// do nothing
} else if (tmp_len > 0 && ctx.org_expr_name_.length() > 0) {
int32_t len = static_cast<int64_t>(tmp_len);
MEMCPY(ctx.param_info_.paramed_field_name_ + ctx.param_info_.name_len_, ctx.org_expr_name_.ptr() + ctx.expr_pos_ - ctx.expr_start_pos_, len);
ctx.param_info_.name_len_ += len;
}
ctx.expr_pos_ = param_node->raw_sql_offset_ + param_node->text_len_;
if (OB_FAIL(ctx.param_info_.questions_pos_.push_back(ctx.param_info_.name_len_))) {
SQL_PC_LOG(WARN, "failed to push back element", K(ret));
} else if (OB_FAIL(ctx.param_info_.params_idx_.push_back(idx))) {
SQL_PC_LOG(WARN, "failed to push back element", K(ret));
} else {
if (ctx.param_info_.name_len_ < ctx.buf_len_) {
ctx.param_info_.paramed_field_name_[ctx.param_info_.name_len_++] = '?'; // 替换常量为'?'
}
if (ctx.tree_->is_neg_ && OB_FAIL(ctx.param_info_.neg_params_idx_.add_member(idx))) {
SQL_PC_LOG(WARN, "failed to add member", K(ret), K(idx));
}
if (OB_SUCC(ret)) {
LOG_DEBUG("resolve a paramed const",
K(ctx.expr_pos_), K(ctx.expr_start_pos_), K(ctx.org_expr_name_),
K(param_node->raw_sql_offset_));
}
}
}
return ret;
}
int ObSqlParameterization::transform_minus_op(ObIAllocator &alloc, ParseNode *tree, bool is_from_pl)
{
int ret = OB_SUCCESS;
if (T_OP_MINUS != tree->type_) {
// do nothing
} else if (2 != tree->num_child_
|| OB_ISNULL(tree->children_)
|| OB_ISNULL(tree->children_[1])) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid minus tree", K(ret));
} else if (1 == tree->children_[1]->is_assigned_from_child_) {
// select 1 - (2) from dual;
// select 1 - (2/3/4) from dual;
// 对于常量节点2,都不能转换成-2
// do nothing
} else if (ob_is_numeric_type(ITEM_TO_OBJ_TYPE(tree->children_[1]->type_)) &&
tree->children_[1]->value_ >= 0) {
ParseNode *child = tree->children_[1];
tree->type_ = T_OP_ADD;
if (!is_from_pl) {
if (T_INT == child->type_) {
child->value_ = -child->value_;
}
char *new_str = static_cast<char *>(parse_malloc(child->str_len_ + 2, &alloc));
char *new_raw_text = static_cast<char *>(parse_malloc(child->text_len_ + 2, &alloc));
if (OB_ISNULL(new_str) || OB_ISNULL(new_raw_text)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to allocate memory", K(ret), K(new_raw_text), K(new_str));
} else {
new_str[0] = '-';
new_raw_text[0] = '-';
MEMMOVE(new_str + 1, child->str_value_, child->str_len_);
MEMMOVE(new_raw_text + 1, child->raw_text_, child->text_len_);
new_str[child->str_len_ + 1] = '\0';
new_raw_text[child->text_len_ + 1] = '\0';
child->str_len_++;
child->text_len_ ++;
child->str_value_ = new_str;
child->raw_text_ = new_raw_text;
child->is_trans_from_minus_ = 1;
}
} else {
child->is_trans_from_minus_ = 1;
}
} else if (T_OP_MUL == tree->children_[1]->type_
|| T_OP_DIV == tree->children_[1]->type_
|| T_OP_INT_DIV == tree->children_[1]->type_
|| (lib::is_mysql_mode() && T_OP_MOD == tree->children_[1]->type_)) {
/* '0 - 2 * 3' should be transformed to '0 + (-2) * 3' */
/* '0 - 2 / 3' should be transformed to '0 + (-2) / 3' */
/* '0 - 4 mod 3' should be transformed to '0 + (-4 mod 3)' */
/* '0 - 2/3/4' => '0 + (-2/3/4)' */
/* notify that, syntax tree of '0 - 2/3/4' is */
/* - */
/* / \ */
/* 0 div */
/* / \ */
/* div 4 */
/* / \ */
/* 2 3 */
/* so, we need to find the leftest leave node and change its value and str */
/* same for '%','*', mod */
/* */
/* 在oracle模式下只有mod函数,比如select 1 - mod(mod(3, 4), 2) from dual; */
/* 语法树为: */
/* - */
/* / \ */
/* 1 mod */
/* / \ */
/* mod 2 */
/* / \ */
/* 3 4 */
/* 这个语法树和mysql模式下的select 1 - 3%4%2 from dual是一样的,但是-和3在oracle模式下不能结合在一起 */
/* 否则快速参数化和硬解析得到的常量不一样(3和-3),所以oracle模式下T_OP_MOD不能转换减号 */
ParseNode *const_node = NULL;
ParseNode *op_node = tree->children_[1];
if (OB_FAIL(find_leftest_const_node(*op_node, const_node))) {
LOG_WARN("failed to find leftest const node", K(ret));
} else if (OB_ISNULL(const_node)) {
// 1 - (2)/3, -和2也是不能结合的
// do nothing
} else {
tree->type_ = T_OP_ADD;
if (!is_from_pl) {
if (T_INT == const_node->type_) {
const_node->value_ = -const_node->value_;
}
char *new_str = static_cast<char *>(parse_malloc(const_node->str_len_ + 2, &alloc));
char *new_raw_text = static_cast<char *>(parse_malloc(const_node->text_len_ + 2, &alloc));
if (OB_ISNULL(new_str) || OB_ISNULL(new_raw_text)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("failed to alloc memory", K(ret), K(new_str), K(new_raw_text));
} else {
new_str[0] = '-';
new_raw_text[0] = '-';
MEMMOVE(new_str + 1, const_node->str_value_, const_node->str_len_);
MEMMOVE(new_raw_text + 1, const_node->raw_text_, const_node->text_len_);
new_str[const_node->str_len_ + 1] = '\0';
new_raw_text[const_node->text_len_]= '\0';
const_node->str_len_++;
const_node->text_len_++;
const_node->str_value_ = new_str;
const_node->raw_text_ = new_raw_text;
const_node->is_trans_from_minus_ = 1;
}
} else {
const_node->is_trans_from_minus_ = 1;
}
}
} else {
// do nothing
}
return ret;
}
int ObSqlParameterization::find_leftest_const_node(ParseNode &cur_node, ParseNode *&const_node)
{
int ret = OB_SUCCESS;
if (ob_is_numeric_type(ITEM_TO_OBJ_TYPE(cur_node.type_))
&& 0 == cur_node.is_assigned_from_child_) {
const_node = &cur_node;
} else if (1 == cur_node.is_assigned_from_child_) {
// do nothing
} else if (T_OP_MUL == cur_node.type_ || T_OP_DIV == cur_node.type_
|| T_OP_INT_DIV == cur_node.type_ || T_OP_MOD == cur_node.type_) {
/* 对于1 - (2-3)/4,语法树为 */
/* - */
/* / \ */
/* 1 div */
/* / \ */
/* - 4 */
/* / \ */
/* 2 3 */
/* 这时候-是不能和2结合的,也就是不能转换语树 */
/* 对于一元操作符(一元操作符优先级大于减操作),比如负号 */
/* 1 - (-2)/4 */
/* 语法树为: */
/* - */
/* / \ */
/* 1 div */
/* / \ */
/* neg 4 */
/* | */
/* 2 */
/* 这种情况下,-也不能2结合 */
/* 所以只有T_OP_MUL、T_OP_DIV和T_OP_MOD走到这条路径上 */
if (OB_ISNULL(cur_node.children_) || 2 != cur_node.num_child_
|| OB_ISNULL(cur_node.children_[0]) || OB_ISNULL(cur_node.children_[1])) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument");
} else if (OB_FAIL(find_leftest_const_node(*cur_node.children_[0], const_node))) {
LOG_WARN("failed to find leftest const node", K(ret));
} else {
// do nothing
}
}
return ret;
}