Files
oceanbase/src/sql/engine/expr/ob_expr_lrpad.cpp
oceanbase-admin cea7de1475 init push
2021-05-31 22:56:52 +08:00

1174 lines
44 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_ENG
#include "sql/engine/expr/ob_expr_lrpad.h"
#include "sql/engine/expr/ob_expr_util.h"
#include "sql/engine/ob_exec_context.h"
#include <limits.h>
#include <string.h>
#include "sql/session/ob_sql_session_info.h"
#include "share/ob_worker.h"
namespace oceanbase {
using namespace oceanbase::common;
using namespace oceanbase::sql;
namespace sql {
/* ObExprBaseLRpad {{{1 */
/* common util {{{2 */
ObExprBaseLRpad::ObExprBaseLRpad(ObIAllocator& alloc, ObExprOperatorType type, const char* name, int32_t param_num)
: ObStringExprOperator(alloc, type, name, param_num)
{}
ObExprBaseLRpad::~ObExprBaseLRpad()
{}
// See: ObExprBaseLRpad::calc_mysql
int ObExprBaseLRpad::calc_type_length_mysql(const ObExprResType result_type, const ObObj& text, const ObObj& pad_text,
const ObObj& len, const ObBasicSessionInfo* session, int64_t& result_size)
{
int ret = OB_SUCCESS;
int64_t max_result_size = OB_MAX_VARCHAR_LENGTH;
int64_t int_len = 0;
int64_t repeat_count = 0;
int64_t prefix_size = 0;
int64_t text_len = 0;
ObString str_text;
ObString str_pad;
result_size = max_result_size;
ObExprCtx expr_ctx;
ObArenaAllocator allocator(common::ObModIds::OB_SQL_EXPR_CALC);
expr_ctx.calc_buf_ = &allocator;
if (OB_NOT_NULL(session) && OB_FAIL(session->get_max_allowed_packet(max_result_size))) {
if (OB_ENTRY_NOT_EXIST == ret) { // for compatibility with server before 1470
ret = OB_SUCCESS;
} else {
LOG_WARN("Failed to get max allow packet size", K(ret));
}
}
if (OB_FAIL(ret)) {
// do nothing
} else if (OB_FAIL(ObExprUtil::get_round_int64(len, expr_ctx, int_len))) {
LOG_WARN("get_round_int64 failed and ignored", K(ret));
ret = OB_SUCCESS;
} else {
if (!ob_is_string_type(text.get_type())) {
result_size = int_len;
} else if (OB_FAIL(text.get_string(str_text))) {
LOG_WARN("Failed to get str_text", K(ret), K(text));
} else if (FALSE_IT(
text_len = ObCharset::strlen_char(
result_type.get_collation_type(), const_cast<const char*>(str_text.ptr()), str_text.length()))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_text));
} else if (text_len >= int_len) {
// only substr needed
result_size = ObCharset::charpos(result_type.get_collation_type(), str_text.ptr(), str_text.length(), int_len);
} else {
if (!ob_is_string_type(pad_text.get_type())) {
result_size = int_len;
} else if (OB_FAIL(pad_text.get_string(str_pad))) {
LOG_WARN("Failed to get str_text", K(ret), K(pad_text));
} else if (str_pad.length() == 0) {
result_size = int_len;
} else if (OB_FAIL(get_padding_info_mysql(result_type.get_collation_type(),
str_text,
int_len,
str_pad,
max_result_size,
repeat_count,
prefix_size,
result_size))) {
LOG_WARN("Failed to get padding info", K(ret), K(str_text), K(int_len), K(str_pad), K(max_result_size));
} else {
result_size = str_text.length() + str_pad.length() * repeat_count + prefix_size;
}
}
}
if (result_size > max_result_size) {
result_size = max_result_size;
}
return ret;
}
// See: ObExprBaseLRpad::calc_oracle
int ObExprBaseLRpad::calc_type_length_oracle(
const ObExprResType& result_type, const ObObj& text, const ObObj& pad_text, const ObObj& len, int64_t& result_size)
{
int ret = OB_SUCCESS;
const int64_t max_result_size = result_type.is_lob() ? OB_MAX_LONGTEXT_LENGTH : OB_MAX_ORACLE_VARCHAR_LENGTH;
int64_t width = 0;
int64_t repeat_count = 0;
int64_t prefix_size = 0;
bool pad_space = false;
int64_t text_width = 0;
ObString str_text;
ObString str_pad;
ObString space_str = ObCharsetUtils::get_const_str(result_type.get_collation_type(), ' ');
ObExprCtx expr_ctx;
ObArenaAllocator allocator(common::ObModIds::OB_SQL_EXPR_CALC);
expr_ctx.calc_buf_ = &allocator;
result_size = max_result_size;
/* get length */
if (OB_FAIL(ObExprUtil::get_trunc_int64(len, expr_ctx, width))) {
LOG_WARN("get_trunc_int64 failed and ignored", K(ret));
ret = OB_SUCCESS;
} else if (width < 0) {
// do nothing
} else {
// both_const_str if true means: a and b are const in rpad(a, count, b)
bool both_const_str = false;
OX(both_const_str = ob_is_string_type(text.get_type()) && ob_is_string_type(pad_text.get_type()));
if (OB_FAIL(ret)) {
} else if (false == both_const_str) {
// when not const, 4 times length in Oracle
result_size = LS_CHAR == result_type.get_length_semantics() ? width : width * 4;
} else {
if (OB_FAIL(text.get_string(str_text))) {
LOG_WARN("Failed to get str_text", K(ret), K(text));
} else if (OB_FAIL(ObCharset::display_len(result_type.get_collation_type(), str_text, text_width))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_text));
} else if (text_width == width) {
result_size = width;
} else if (text_width > width) {
// substr
int64_t total_width = 0;
pad_space = true;
if (OB_FAIL(ObCharset::max_display_width_charpos(result_type.get_collation_type(),
str_text.ptr(),
str_text.length(),
width,
prefix_size,
&total_width))) {
} else {
pad_space = (total_width != width);
result_size = prefix_size + (pad_space ? space_str.length() : 0);
}
} else if (OB_FAIL(pad_text.get_string(str_pad))) {
LOG_WARN("Failed to get str_text", K(ret), K(pad_text));
} else if (OB_FAIL(get_padding_info_oracle(result_type.get_collation_type(),
str_text,
width,
str_pad,
max_result_size,
repeat_count,
prefix_size,
pad_space))) {
LOG_WARN("Failed to get padding info", K(ret), K(str_text), K(width), K(str_pad), K(max_result_size));
} else {
ObCollationType cs_type = result_type.get_collation_type();
if (LS_CHAR != result_type.get_length_semantics()) {
result_size =
str_text.length() + str_pad.length() * repeat_count + prefix_size + (pad_space ? space_str.length() : 0);
} else {
result_size = ObCharset::strlen_char(cs_type, str_text.ptr(), str_text.length()) +
ObCharset::strlen_char(cs_type, str_pad.ptr(), str_pad.length()) * repeat_count + prefix_size +
(pad_space ? space_str.length() : 0);
}
}
}
}
if (result_size > max_result_size) {
result_size = max_result_size;
}
return ret;
}
int ObExprBaseLRpad::calc_type(
ObExprResType& type, ObExprResType& text, ObExprResType& len, ObExprResType* pad_text, ObExprTypeCtx& type_ctx)
{
int ret = OB_SUCCESS;
ObObjType text_type = ObNullType;
ObObjType len_type = ObNullType;
const bool is_oracle_mode = share::is_oracle_mode();
int64_t max_len = OB_MAX_VARCHAR_LENGTH;
int64_t text_len = text.get_length();
if (is_oracle_mode) {
len_type = ObNumberType;
if (text.is_nstring()) {
text_type = ObNVarchar2Type;
max_len = OB_MAX_ORACLE_VARCHAR_LENGTH;
} else if (!text.is_lob()) {
text_type = ObVarcharType;
max_len = OB_MAX_ORACLE_VARCHAR_LENGTH;
} else if (text.is_blob()) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("Blob type in LRpad not supported", K(ret), K(text.get_type()));
} else {
text_type = ObLongTextType;
max_len = OB_MAX_LONGTEXT_LENGTH;
}
} else if (share::is_mysql_mode()) {
len_type = ObIntType;
text_type = ObVarcharType;
max_len = OB_MAX_VARCHAR_LENGTH;
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("error compat mode", K(ret));
}
const ObSQLSessionInfo* session = type_ctx.get_session();
CK(OB_NOT_NULL(session));
if (OB_SUCC(ret)) {
ObObj length_obj = len.get_param();
ObObj text_obj = text.get_param();
ObObj pad_obj;
ObString default_pad_str = ObString(" "); // default ' '
type.set_length(static_cast<ObLength>(max_len));
len.set_calc_type(len_type);
if (NULL != pad_text) {
if (is_mysql_mode()) {
pad_obj = pad_text->get_param();
type.set_type(text_type);
text.set_calc_type(text_type);
pad_text->set_calc_type(text_type);
ObSEArray<ObExprResType, 2> types;
OZ(types.push_back(text));
OZ(types.push_back(*pad_text));
OZ(aggregate_charsets_for_string_result(type, &types.at(0), 2, type_ctx.get_coll_type()));
OX(text.set_calc_collation_type(type.get_collation_type()));
OX(pad_text->set_calc_collation_type(type.get_collation_type()));
} else {
OX(pad_obj = pad_text->get_param());
ObSEArray<ObExprResType*, 1, ObNullAllocator> types;
OZ(types.push_back(&text));
OZ(aggregate_string_type_and_charset_oracle(*session, types, type, true));
OX(text.set_calc_meta(type));
OX(pad_text->set_calc_meta(type));
}
} else {
type.set_type(text_type);
text.set_calc_type(text_type);
pad_obj.set_string(ObVarcharType, default_pad_str);
pad_obj.set_collation_type(ObCharset::get_system_collation());
if (text.is_string_type()) {
type.set_collation_type(text.get_collation_type());
type.set_collation_level(text.get_collation_level());
} else {
type.set_collation_type(get_default_collation_type(type.get_type(), *type_ctx.get_session()));
type.set_collation_level(CS_LEVEL_IMPLICIT);
}
text.set_calc_collation_type(type.get_collation_type());
}
const int64_t buf_len = pad_obj.is_character_type() ? pad_obj.get_string_len() * 4 : 0;
char buf[buf_len];
if (OB_SUCC(ret) && share::is_oracle_mode() && !pad_obj.is_null_oracle() && pad_obj.is_character_type()) {
ObDataBuffer data_buf(buf, buf_len);
if (OB_FAIL(convert_result_collation(type, pad_obj, &data_buf))) {
LOG_WARN("fail to convert result_collation", K(ret));
}
}
if (OB_SUCC(ret)) {
if (OB_NOT_NULL(type_ctx.get_session()) && is_oracle_mode) {
if (type.is_nvarchar2()) {
type.set_length_semantics(LS_CHAR);
} else {
const ObLengthSemantics default_length_semantics =
(OB_NOT_NULL(type_ctx.get_session()) ? type_ctx.get_session()->get_actual_nls_length_semantics()
: LS_BYTE);
type.set_length_semantics(default_length_semantics);
}
}
if (!length_obj.is_null()) {
if (is_oracle_mode && OB_FAIL(calc_type_length_oracle(type, text_obj, pad_obj, length_obj, text_len))) {
LOG_WARN("failed to calc result type length oracle mode", K(ret));
} else if (!is_oracle_mode && OB_FAIL(calc_type_length_mysql(
type, text_obj, pad_obj, length_obj, type_ctx.get_session(), text_len))) {
LOG_WARN("failed to calc result type length mysql mode", K(ret));
}
} else {
text_len = max_len;
}
text_len = (text_len > max_len) ? max_len : text_len;
type.set_length(static_cast<ObLength>(text_len));
LOG_DEBUG("ObExprBaseLRpad::calc_type()",
K(ret),
K(text),
K(text_obj),
K(pad_obj),
K(len),
K(length_obj),
KP(pad_text),
K(type),
K(text_len),
K(max_len),
K(type.get_length_semantics()));
}
}
return ret;
}
int ObExprBaseLRpad::padding(LRpadType type, const ObCollationType coll_type, const char* text,
const int64_t& text_size, const char* pad, const int64_t& pad_size, const int64_t& prefix_size,
const int64_t& repeat_count,
const bool& pad_space, // for oracle
ObIAllocator* allocator, char*& result, int64_t& size)
{
int ret = OB_SUCCESS;
ObString space_str = ObCharsetUtils::get_const_str(coll_type, ' ');
// start pos
char* text_start_pos = NULL;
char* pad_start_pos = NULL;
char* sp_start_pos = NULL;
size = text_size + pad_size * repeat_count + prefix_size + (pad_space ? space_str.length() : 0);
if (OB_UNLIKELY(size <= 0) || OB_UNLIKELY(repeat_count < 0) || OB_UNLIKELY(pad_size <= 0) ||
OB_UNLIKELY(prefix_size >= pad_size) || (OB_ISNULL(text) && text_size != 0) || OB_ISNULL(pad) ||
OB_ISNULL(allocator)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Wrong param",
K(ret),
K(size),
K(repeat_count),
K(pad_size),
K(prefix_size),
K(text),
K(text_size),
K(pad),
K(allocator));
} else if (OB_ISNULL(result = static_cast<char*>(allocator->alloc(size)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("Failed to alloc", K(ret));
} else if (type == LPAD_TYPE) {
// lpad: [sp] + padtext * t + padprefix + text
if (pad_space) {
sp_start_pos = result;
pad_start_pos = result + space_str.length();
} else {
pad_start_pos = result;
}
text_start_pos = pad_start_pos + (pad_size * repeat_count + prefix_size);
} else if (type == RPAD_TYPE) {
// rpad: text + padtext * t + padprefix + [sp]
text_start_pos = result;
pad_start_pos = text_start_pos + text_size;
if (pad_space) {
sp_start_pos = pad_start_pos + (pad_size * repeat_count + prefix_size);
}
}
if (OB_SUCC(ret)) {
// place pad string
for (int64_t i = 0; i < repeat_count; i++) {
MEMCPY(pad_start_pos + i * pad_size, pad, pad_size);
}
// place pad string prefix
MEMCPY(pad_start_pos + repeat_count * pad_size, pad, prefix_size);
// place text string
MEMCPY(text_start_pos, text, text_size);
if (pad_space) {
MEMCPY(sp_start_pos, space_str.ptr(), space_str.length());
}
}
return ret;
}
int ObExprBaseLRpad::calc(const LRpadType type, const ObObj& text, const ObObj& len, const ObObj& pad_text,
ObExprCtx& expr_ctx, ObObj& result) const
{
int ret = OB_SUCCESS;
if (share::is_mysql_mode()) {
if (OB_FAIL(
calc_mysql(type, result_type_, text, len, pad_text, expr_ctx.my_session_, expr_ctx.calc_buf_, result))) {
LOG_WARN("Failed to calc mysql",
K(type),
K(text),
K(len),
K(pad_text),
K(expr_ctx.my_session_),
K(expr_ctx.calc_buf_));
}
} else if (share::is_oracle_mode()) {
if (OB_FAIL(calc_oracle(type, result_type_, text, len, pad_text, expr_ctx, result))) {
LOG_WARN("Failed to calc oracle", K(type), K(text), K(len), K(pad_text), K(expr_ctx.calc_buf_));
}
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("error compat mode", K(ret));
}
return ret;
}
/* common util END }}} */
/* mysql util {{{2 */
int ObExprBaseLRpad::calc_mysql(const LRpadType type, const ObExprResType result_type, const ObObj& text,
const ObObj& len, const ObObj& pad_text, const ObSQLSessionInfo* session, ObIAllocator* allocator, ObObj& result)
{
int ret = OB_SUCCESS;
int64_t max_result_size = -1;
int64_t int_len = 0;
int64_t repeat_count = 0;
int64_t prefix_size = 0;
int64_t text_len = 0;
ObString str_text;
ObString str_pad;
char* result_ptr = NULL;
int64_t result_size = 0;
if (OB_ISNULL(session) || OB_ISNULL(allocator)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Unexpected null session or allocator in Lpad function", K(ret), K(session), K(allocator));
} else if (OB_FAIL(session->get_max_allowed_packet(max_result_size))) {
if (OB_ENTRY_NOT_EXIST == ret) { // for compatibility with server before 1470
ret = OB_SUCCESS;
max_result_size = OB_MAX_VARCHAR_LENGTH;
} else {
LOG_WARN("Failed to get max allow packet size", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (text.is_null() || len.is_null() || pad_text.is_null()) {
result.set_null();
} else {
TYPE_CHECK(len, ObIntType);
if (OB_FAIL(len.get_int(int_len))) {
LOG_WARN("Failed to get len", K(ret), K(len));
} else if (int_len < 0) {
result.set_null();
} else if (int_len == 0) {
result.set_varchar(ObString::make_empty_string());
result.set_collation(result_type);
} else if (OB_FAIL(text.get_string(str_text))) {
LOG_WARN("Failed to get str_text", K(ret), K(text));
} else if (OB_FAIL(pad_text.get_string(str_pad))) {
LOG_WARN("Failed to get str_text", K(ret), K(pad_text));
} else if (FALSE_IT(
text_len = ObCharset::strlen_char(
result_type.get_collation_type(), const_cast<const char*>(str_text.ptr()), str_text.length()))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_text));
} else if (text_len >= int_len) {
// only substr needed
result_size = ObCharset::charpos(result_type.get_collation_type(), str_text.ptr(), str_text.length(), int_len);
result_ptr = static_cast<char*>(allocator->alloc(result_size));
if (OB_ISNULL(result_ptr)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("Failed to alloc", K(ret));
} else {
MEMCPY(result_ptr, str_text.ptr(), result_size);
result.set_varchar(result_ptr, static_cast<ObString::obstr_size_t>(result_size));
result.set_collation(result_type);
}
} else if (str_pad.length() == 0) {
result.set_null();
} else if (OB_FAIL(get_padding_info_mysql(result_type.get_collation_type(),
str_text,
int_len,
str_pad,
max_result_size,
repeat_count,
prefix_size,
result_size))) {
LOG_WARN("Failed to get padding info", K(ret), K(str_text), K(int_len), K(str_pad), K(max_result_size));
} else if (result_size > max_result_size) {
result.set_null();
if (type == RPAD_TYPE) {
LOG_USER_WARN(OB_ERR_FUNC_RESULT_TOO_LARGE, "rpad", static_cast<int>(max_result_size));
} else {
LOG_USER_WARN(OB_ERR_FUNC_RESULT_TOO_LARGE, "lpad", static_cast<int>(max_result_size));
}
} else if (OB_FAIL(padding(type,
result_type.get_collation_type(),
str_text.ptr(),
str_text.length(),
str_pad.ptr(),
str_pad.length(),
prefix_size,
repeat_count,
false,
allocator,
result_ptr,
result_size))) {
LOG_WARN("Failed to pad", K(ret), K(str_text), K(str_pad), K(prefix_size), K(repeat_count));
} else {
result.set_string(result_type.get_type(), result_ptr, static_cast<ObString::obstr_size_t>(result_size));
result.set_collation(result_type);
}
}
return ret;
}
int ObExprBaseLRpad::get_padding_info_mysql(const ObCollationType& cs, const ObString& str_text, const int64_t& len,
const ObString& str_padtext, const int64_t max_result_size, int64_t& repeat_count, int64_t& prefix_size,
int64_t& size)
{
// lpad: [sp] + padtext * t + padprefix + text
// rpad: text + padtext * t + padprefix + [sp]
int ret = OB_SUCCESS;
int64_t text_size = str_text.length();
int64_t pad_size = str_padtext.length();
// GOAL: get repeat_count, prefix_size and pad space.
int64_t text_len = ObCharset::strlen_char(cs, const_cast<const char*>(str_text.ptr()), str_text.length());
int64_t pad_len = ObCharset::strlen_char(cs, const_cast<const char*>(str_padtext.ptr()), str_padtext.length());
if (OB_UNLIKELY(len <= text_len) || OB_UNLIKELY(len <= 0) || OB_UNLIKELY(pad_len <= 0) ||
OB_UNLIKELY(pad_size <= 0)) {
// this should been resolve outside
ret = OB_ERR_UNEXPECTED;
LOG_WARN("wrong len", K(ret), K(len), K(text_len));
} else {
repeat_count = std::min((len - text_len) / pad_len, (max_result_size - text_size) / pad_size);
int64_t remain_len = len - (text_len + pad_len * repeat_count);
prefix_size = ObCharset::charpos(cs, const_cast<const char*>(str_padtext.ptr()), str_padtext.length(), remain_len);
size = text_size + pad_size * repeat_count + prefix_size;
}
return ret;
}
// for engine 3.0
int ObExprBaseLRpad::calc_mysql_pad_expr(const ObExpr& expr, ObEvalCtx& ctx, LRpadType pad_type, ObDatum& res)
{
int ret = OB_SUCCESS;
ObDatum* text = NULL;
ObDatum* len = NULL;
ObDatum* pad_text = NULL;
if (OB_UNLIKELY(3 != expr.arg_cnt_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("arg cnt must be 3", K(ret), K(expr.arg_cnt_));
} else if (OB_FAIL(expr.eval_param_value(ctx, text, len, pad_text))) {
LOG_WARN("eval param value failed", K(ret));
} else {
const ObSQLSessionInfo* session = ctx.exec_ctx_.get_my_session();
ObExprStrResAlloc res_alloc(expr, ctx); // make sure alloc() is called only once
if (OB_ISNULL(session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("session is NULL", K(ret));
} else if (OB_FAIL(calc_mysql(pad_type, expr, *text, *len, *pad_text, *session, res_alloc, res))) {
LOG_WARN("calc_mysql failed", K(ret));
}
}
return ret;
}
int ObExprBaseLRpad::calc_mysql(const LRpadType pad_type, const ObExpr& expr, const ObDatum& text, const ObDatum& len,
const ObDatum& pad_text, const ObSQLSessionInfo& session, ObIAllocator& res_alloc, ObDatum& res)
{
int ret = OB_SUCCESS;
int64_t max_result_size = -1;
int64_t repeat_count = 0;
int64_t prefix_size = 0;
int64_t text_len = 0;
char* result_ptr = NULL;
int64_t result_size = 0;
const ObCollationType cs_type = expr.datum_meta_.cs_type_;
if (OB_FAIL(session.get_max_allowed_packet(max_result_size))) {
if (OB_ENTRY_NOT_EXIST == ret) { // for compatibility with server before 1470
ret = OB_SUCCESS;
max_result_size = OB_MAX_VARCHAR_LENGTH;
} else {
LOG_WARN("Failed to get max allow packet size", K(ret));
}
}
if (OB_FAIL(ret)) {
} else if (text.is_null() || len.is_null() || pad_text.is_null()) {
res.set_null();
} else {
int64_t int_len = len.get_int();
const ObString& str_text = text.get_string();
const ObString& str_pad = pad_text.get_string();
if (int_len < 0) {
res.set_null();
} else if (int_len == 0) {
res.set_string(ObString::make_empty_string());
} else if (FALSE_IT(text_len = ObCharset::strlen_char(
cs_type, const_cast<const char*>(str_text.ptr()), str_text.length()))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_text));
} else if (text_len >= int_len) {
// only substr needed
result_size = ObCharset::charpos(cs_type, str_text.ptr(), str_text.length(), int_len);
res.set_string(ObString(result_size, str_text.ptr()));
} else if (str_pad.length() == 0) {
res.set_null();
} else if (OB_FAIL(get_padding_info_mysql(
cs_type, str_text, int_len, str_pad, max_result_size, repeat_count, prefix_size, result_size))) {
LOG_WARN("Failed to get padding info", K(ret), K(str_text), K(int_len), K(str_pad), K(max_result_size));
} else if (result_size > max_result_size) {
res.set_null();
if (pad_type == RPAD_TYPE) {
LOG_USER_WARN(OB_ERR_FUNC_RESULT_TOO_LARGE, "rpad", static_cast<int>(max_result_size));
} else {
LOG_USER_WARN(OB_ERR_FUNC_RESULT_TOO_LARGE, "lpad", static_cast<int>(max_result_size));
}
} else if (OB_FAIL(padding(pad_type,
cs_type,
str_text.ptr(),
str_text.length(),
str_pad.ptr(),
str_pad.length(),
prefix_size,
repeat_count,
false,
&res_alloc,
result_ptr,
result_size))) {
LOG_WARN("Failed to pad", K(ret), K(str_text), K(str_pad), K(prefix_size), K(repeat_count));
} else {
if (NULL == result_ptr || 0 == result_size) {
res.set_null();
} else {
res.set_string(result_ptr, result_size);
}
}
}
return ret;
}
/* mysql util END }}} */
/* oracle util {{{2 */
int ObExprBaseLRpad::calc_oracle(const LRpadType type, const ObExprResType result_type, const ObObj& text,
const ObObj& len, const ObObj& pad_text, common::ObExprCtx& expr_ctx, ObObj& result)
{
int ret = OB_SUCCESS;
int64_t max_result_size = result_type.is_lob() ? OB_MAX_LONGTEXT_LENGTH : OB_MAX_ORACLE_VARCHAR_LENGTH;
int64_t width = 0;
int64_t repeat_count = 0;
int64_t prefix_size = 0;
bool pad_space = false;
int64_t text_width = 0;
ObString str_text;
ObString str_pad;
char* result_ptr = NULL;
int64_t result_size = 0;
ObIAllocator* allocator = expr_ctx.calc_buf_;
int64_t tmp_pad_len = 0;
if (text.is_null_oracle() || len.is_null_oracle() || pad_text.is_null_oracle()) {
result.set_null();
} else if (OB_UNLIKELY(!text.is_string_type()) // clob or varchar
|| OB_UNLIKELY(!len.is_number()) // number
|| OB_UNLIKELY(!pad_text.is_string_type()) || OB_ISNULL(allocator)) { // clob or varchar
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Wrong param", K(ret), K(text.get_type()), K(len.get_type()), K(pad_text.get_type()), K(allocator));
} else if (text.is_blob()) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("Blob type in LRpad not supported", K(ret), K(text.get_type()));
} else if (OB_FAIL(ObExprUtil::get_trunc_int64(len, expr_ctx, tmp_pad_len))) {
LOG_WARN("fail to get pad_len", K(ret), K(len));
} else if (text.is_clob() && pad_text.is_clob() && (0 == pad_text.val_len_) && (text.val_len_ <= tmp_pad_len)) {
result.set_string(result_type.get_type(), text.get_string());
result.set_collation(result_type);
} else {
/* get length */
number::ObNumber len_num;
int64_t decimal_parts = -1;
if (OB_FAIL(len.get_number(len_num))) {
LOG_WARN("failed to get length", K(ret));
} else if (len_num.is_negative()) {
width = -1;
} else if (!len_num.is_int_parts_valid_int64(width, decimal_parts)) {
width = UINT32_MAX;
}
if (OB_FAIL(ret)) {
} else if (width <= 0) {
result.set_null();
} else if (OB_FAIL(text.get_string(str_text))) {
LOG_WARN("Failed to get str_text", K(ret), K(text));
} else if (OB_FAIL(pad_text.get_string(str_pad))) {
LOG_WARN("Failed to get str_text", K(ret), K(pad_text));
} else if (OB_FAIL(ObCharset::display_len(result_type.get_collation_type(), str_text, text_width))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_text));
} else {
if (text_width == width) {
result = text;
result_ptr = const_cast<char*>(text.get_string_ptr());
result_size = text.get_string_len();
} else if (text_width > width) {
// substr
int64_t total_width = 0;
if (OB_FAIL(ObCharset::max_display_width_charpos(result_type.get_collation_type(),
str_text.ptr(),
str_text.length(),
width,
prefix_size,
&total_width))) {
LOG_WARN("Failed to get max display width", K(ret));
} else if (OB_FAIL(padding(type,
result_type.get_collation_type(),
"",
0,
str_text.ptr(),
str_text.length(),
prefix_size,
0,
(total_width != width),
allocator,
result_ptr,
result_size))) {
LOG_WARN("Failed to pad", K(ret), K(str_text), K(str_pad), K(prefix_size), K(repeat_count), K(pad_space));
}
} else if (OB_FAIL(get_padding_info_oracle(result_type.get_collation_type(),
str_text,
width,
str_pad,
max_result_size,
repeat_count,
prefix_size,
pad_space))) {
LOG_WARN("Failed to get padding info", K(ret), K(str_text), K(width), K(str_pad), K(max_result_size));
} else if (OB_FAIL(padding(type,
result_type.get_collation_type(),
str_text.ptr(),
str_text.length(),
str_pad.ptr(),
str_pad.length(),
prefix_size,
repeat_count,
pad_space,
allocator,
result_ptr,
result_size))) {
LOG_WARN("Failed to pad", K(ret), K(str_text), K(str_pad), K(prefix_size), K(repeat_count), K(pad_space));
}
if (OB_SUCC(ret)) {
result.set_string(result_type.get_type(), result_ptr, static_cast<ObString::obstr_size_t>(result_size));
result.set_collation(result_type);
}
}
}
return ret;
}
int ObExprBaseLRpad::get_padding_info_oracle(const ObCollationType cs, const ObString& str_text, const int64_t& width,
const ObString& str_padtext, const int64_t max_result_size, int64_t& repeat_count, int64_t& prefix_size,
bool& pad_space)
{
// lpad: [sp] + padtext * t + padprefix + text
// rpad: text + padtext * t + padprefix + [sp]
int ret = OB_SUCCESS;
int64_t text_size = str_text.length();
int64_t pad_size = str_padtext.length();
int64_t text_width = 0;
int64_t pad_width = 0;
pad_space = false;
// GOAL: get repeat_count, prefix_size and pad space.
if (OB_FAIL(ObCharset::display_len(cs, str_text, text_width))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_text));
} else if (OB_FAIL(ObCharset::display_len(cs, str_padtext, pad_width))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_padtext));
} else if (OB_UNLIKELY(width <= text_width) || OB_UNLIKELY(width <= 0) || OB_UNLIKELY(pad_size <= 0) ||
OB_UNLIKELY(pad_width <= 0)) {
// this should been resolve outside
ret = OB_ERR_UNEXPECTED;
LOG_WARN("wrong width", K(ret), K(width), K(text_width), K(pad_size), K(pad_width));
} else {
repeat_count = std::min((width - text_width) / pad_width, (max_result_size - text_size) / pad_size);
int64_t remain_width = width - (text_width + repeat_count * pad_width);
int64_t remain_size = max_result_size - (text_size + repeat_count * pad_size);
int64_t total_width = 0;
LOG_DEBUG("calc pad",
K(remain_width),
K(width),
K(text_width),
K(pad_width),
K(max_result_size),
K(text_size),
K(pad_size),
K(ret),
K(remain_size));
if (remain_width > 0 && remain_size > 0) {
if (OB_FAIL(ObCharset::max_display_width_charpos(
cs, str_padtext.ptr(), std::min(remain_size, pad_size), remain_width, prefix_size, &total_width))) {
LOG_WARN("Failed to get max display width", K(ret), K(str_text), K(remain_width));
} else if (remain_width != total_width && remain_size != prefix_size) {
pad_space = true;
}
}
}
return ret;
}
// for engine 3.0
int ObExprBaseLRpad::calc_oracle_pad_expr(const ObExpr& expr, ObEvalCtx& ctx, LRpadType pad_type, ObDatum& res)
{
int ret = OB_SUCCESS;
ObDatum* text = NULL;
ObDatum* len = NULL;
ObDatum* pad_text = NULL;
if (OB_FAIL(expr.eval_param_value(ctx, text, len, pad_text))) {
LOG_WARN("eval param failed", K(ret));
} else {
ObExprStrResAlloc res_alloc(expr, ctx);
if (3 == expr.arg_cnt_) {
if (OB_ISNULL(text) || OB_ISNULL(len) || OB_ISNULL(pad_text)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected datum", K(ret), KP(text), KP(len), KP(pad_text));
} else if (OB_FAIL(calc_oracle(pad_type, expr, *text, *len, *pad_text, res_alloc, res))) {
LOG_WARN("calc pad failed", K(ret));
}
} else if (2 == expr.arg_cnt_) {
ObCollationType in_coll = ObCharset::get_system_collation();
ObCollationType out_coll = expr.datum_meta_.cs_type_;
ObIAllocator& calc_alloc = ctx.get_reset_tmp_alloc();
ObString pad_str_utf8(1, " ");
ObString pad_str;
ObDatum tmp_pad_text;
tmp_pad_text.set_string(pad_str);
if (OB_ISNULL(text) || OB_ISNULL(len)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected datum", K(ret), KP(text), KP(len));
} else if (OB_FAIL(ObExprUtil::convert_string_collation(pad_str_utf8, in_coll, pad_str, out_coll, calc_alloc))) {
LOG_WARN("convert collation failed", K(ret), K(in_coll), K(pad_str), K(out_coll));
} else if (OB_UNLIKELY(pad_str.empty())) {
LOG_WARN("unexpected pad_str after convert collation", K(ret), K(pad_str));
} else {
tmp_pad_text.set_string(pad_str);
if (OB_FAIL(calc_oracle(pad_type, expr, *text, *len, tmp_pad_text, res_alloc, res))) {
LOG_WARN("calc pad failed", K(ret));
}
}
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid arg cnt", K(ret), K(expr.arg_cnt_));
}
}
return ret;
}
int ObExprBaseLRpad::calc_oracle(LRpadType pad_type, const ObExpr& expr, const ObDatum& text, const ObDatum& len,
const ObDatum& pad_text, ObIAllocator& res_alloc, ObDatum& res)
{
int ret = OB_SUCCESS;
if (text.is_null() || len.is_null() || pad_text.is_null()) {
res.set_null();
} else {
int64_t width = 0;
int64_t repeat_count = 0;
int64_t prefix_size = 0;
bool pad_space = false;
int64_t text_width = 0;
const ObString& str_text = text.get_string();
const ObString& str_pad = pad_text.get_string();
int64_t max_result_size =
ob_is_text_tc(expr.datum_meta_.type_) ? OB_MAX_LONGTEXT_LENGTH : OB_MAX_ORACLE_VARCHAR_LENGTH;
const ObCollationType cs_type = expr.datum_meta_.cs_type_;
number::ObNumber len_num(len.get_number());
int64_t decimal_parts = -1;
if (len_num.is_negative()) {
width = -1;
} else if (!len_num.is_int_parts_valid_int64(width, decimal_parts)) {
width = UINT32_MAX;
}
if (width <= 0) {
res.set_null();
} else if (OB_FAIL(ObCharset::display_len(cs_type, str_text, text_width))) {
LOG_WARN("Failed to get displayed length", K(ret), K(str_text));
} else if ((3 == expr.arg_cnt_) && expr.args_[0]->datum_meta_.is_clob() && (0 == str_pad.length()) &&
(text_width <= width)) {
res.set_datum(text);
} else if (text_width == width) {
res.set_datum(text);
} else {
char* result_ptr = NULL;
int64_t result_size = 0;
if (text_width > width) {
// substr
int64_t total_width = 0;
if (OB_FAIL(ObCharset::max_display_width_charpos(
cs_type, str_text.ptr(), str_text.length(), width, prefix_size, &total_width))) {
LOG_WARN("Failed to get max display width", K(ret));
} else if (OB_FAIL(padding(pad_type,
cs_type,
"",
0,
str_text.ptr(),
str_text.length(),
prefix_size,
0,
(total_width != width),
&res_alloc,
result_ptr,
result_size))) {
LOG_WARN("Failed to pad", K(ret), K(str_text), K(str_pad), K(prefix_size), K(repeat_count), K(pad_space));
}
} else if (OB_FAIL(get_padding_info_oracle(
cs_type, str_text, width, str_pad, max_result_size, repeat_count, prefix_size, pad_space))) {
LOG_WARN("Failed to get padding info", K(ret), K(str_text), K(width), K(str_pad), K(max_result_size));
} else if (OB_FAIL(padding(pad_type,
cs_type,
str_text.ptr(),
str_text.length(),
str_pad.ptr(),
str_pad.length(),
prefix_size,
repeat_count,
pad_space,
&res_alloc,
result_ptr,
result_size))) {
LOG_WARN("Failed to pad", K(ret), K(str_text), K(str_pad), K(prefix_size), K(repeat_count), K(pad_space));
}
if (OB_SUCC(ret)) {
if (NULL == result_ptr || 0 == result_size) {
res.set_null();
} else {
res.set_string(result_ptr, result_size);
}
}
}
}
return ret;
}
/* oracle util END }}} */
/* ObExprBaseLRpad END }}} */
/* ObExprLpad {{{1 */
ObExprLpad::ObExprLpad(ObIAllocator& alloc) : ObExprBaseLRpad(alloc, T_FUN_SYS_LPAD, N_LPAD, 3)
{}
ObExprLpad::~ObExprLpad()
{}
int ObExprLpad::calc_result_type3(ObExprResType& type, ObExprResType& text, ObExprResType& len, ObExprResType& pad_text,
ObExprTypeCtx& type_ctx) const
{
return ObExprBaseLRpad::calc_type(type, text, len, &pad_text, type_ctx);
}
int ObExprLpad::calc_result3(
ObObj& result, const ObObj& text, const ObObj& len, const ObObj& pad_text, ObExprCtx& expr_ctx) const
{
return ObExprBaseLRpad::calc(LPAD_TYPE, text, len, pad_text, expr_ctx, result);
}
int ObExprLpad::cg_expr(ObExprCGCtx& expr_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
int ret = OB_SUCCESS;
UNUSED(expr_cg_ctx);
UNUSED(raw_expr);
rt_expr.eval_func_ = calc_mysql_lpad_expr;
return ret;
}
int ObExprLpad::calc_mysql_lpad_expr(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res)
{
return calc_mysql_pad_expr(expr, ctx, LPAD_TYPE, res);
}
/* ObExprLpad END }}} */
/* ObExprRpad {{{1 */
ObExprRpad::ObExprRpad(ObIAllocator& alloc) : ObExprBaseLRpad(alloc, T_FUN_SYS_RPAD, N_RPAD, 3)
{}
ObExprRpad::ObExprRpad(ObIAllocator& alloc, ObExprOperatorType type, const char* name)
: ObExprBaseLRpad(alloc, type, name, 3)
{}
ObExprRpad::~ObExprRpad()
{}
int ObExprRpad::calc_result_type3(ObExprResType& type, ObExprResType& text, ObExprResType& len, ObExprResType& pad_text,
ObExprTypeCtx& type_ctx) const
{
return ObExprBaseLRpad::calc_type(type, text, len, &pad_text, type_ctx);
}
int ObExprRpad::calc_result3(
ObObj& result, const ObObj& text, const ObObj& len, const ObObj& pad_text, ObExprCtx& expr_ctx) const
{
return ObExprBaseLRpad::calc(RPAD_TYPE, text, len, pad_text, expr_ctx, result);
}
int ObExprRpad::cg_expr(ObExprCGCtx& expr_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
int ret = OB_SUCCESS;
UNUSED(expr_cg_ctx);
UNUSED(raw_expr);
rt_expr.eval_func_ = calc_mysql_rpad_expr;
return ret;
}
int ObExprRpad::calc_mysql_rpad_expr(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res)
{
return calc_mysql_pad_expr(expr, ctx, RPAD_TYPE, res);
}
/* ObExprRpad END }}} */
/* ObExprLpadOracle {{{1 */
ObExprOracleLpad::ObExprOracleLpad(ObIAllocator& alloc) : ObExprBaseLRpad(alloc, T_FUN_SYS_LPAD, N_LPAD, TWO_OR_THREE)
{}
ObExprOracleLpad::~ObExprOracleLpad()
{}
int ObExprOracleLpad::calc_result_typeN(
ObExprResType& type, ObExprResType* types_array, int64_t param_num, ObExprTypeCtx& type_ctx) const
{
int ret = OB_SUCCESS;
if (param_num == 3) {
if (OB_ISNULL(types_array) || OB_ISNULL(types_array + 1) || OB_ISNULL(types_array + 2)) {
LOG_WARN("NULL param", K(ret), K(types_array[0]), K(types_array[1]), K(types_array[2]));
} else if (OB_FAIL(ObExprBaseLRpad::calc_type(type, types_array[0], types_array[1], types_array + 2, type_ctx))) {
LOG_WARN("Failed to calc_type", K(ret));
}
} else if (param_num == 2) {
if (OB_ISNULL(types_array) || OB_ISNULL(types_array + 1)) {
LOG_WARN("NULL param", K(ret), K(types_array[0]), K(types_array[1]));
} else if (OB_FAIL(ObExprBaseLRpad::calc_type(type, types_array[0], types_array[1], NULL, type_ctx))) {
LOG_WARN("Failed to calc_type", K(ret));
}
} else {
ret = OB_NOT_SUPPORTED;
LOG_WARN("Wrong param num", K(ret), K(param_num));
}
return ret;
}
int ObExprOracleLpad::calc_resultN(ObObj& result, const ObObj* objs_array, int64_t param_num, ObExprCtx& expr_ctx) const
{
int ret = OB_SUCCESS;
if (param_num == 3) {
if (OB_ISNULL(objs_array) || OB_ISNULL(objs_array + 1) || OB_ISNULL(objs_array + 2)) {
LOG_WARN("NULL param", K(ret), K(objs_array[0]), K(objs_array[1]), K(objs_array[2]));
} else if (OB_FAIL(
ObExprBaseLRpad::calc(LPAD_TYPE, objs_array[0], objs_array[1], objs_array[2], expr_ctx, result))) {
LOG_WARN("Failed to calc", K(ret));
}
} else if (param_num == 2) {
ObObj pad_text;
pad_text.set_string(ObVarcharType, " ", 1L);
pad_text.set_collation_type(ObCharset::get_system_collation());
if (OB_ISNULL(objs_array) || OB_ISNULL(objs_array + 1)) {
LOG_WARN("NULL param", K(ret), K(objs_array[0]), K(objs_array[1]));
} else if (OB_FAIL(convert_result_collation(result_type_, pad_text, expr_ctx.calc_buf_))) {
LOG_WARN("convert result collation failed", K(ret));
} else if (OB_FAIL(ObExprBaseLRpad::calc(LPAD_TYPE, objs_array[0], objs_array[1], pad_text, expr_ctx, result))) {
LOG_WARN("Failed to calc", K(ret));
}
} else {
ret = OB_NOT_SUPPORTED;
LOG_WARN("Wrong param num", K(ret), K(param_num));
}
return ret;
}
int ObExprOracleLpad::cg_expr(ObExprCGCtx& expr_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
int ret = OB_SUCCESS;
UNUSED(expr_cg_ctx);
UNUSED(raw_expr);
rt_expr.eval_func_ = calc_oracle_lpad_expr;
return ret;
}
int ObExprOracleLpad::calc_oracle_lpad_expr(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res)
{
return calc_oracle_pad_expr(expr, ctx, LPAD_TYPE, res);
}
/* ObExprLpadOracle END }}} */
/* ObExprRpadOracle {{{1 */
ObExprOracleRpad::ObExprOracleRpad(ObIAllocator& alloc) : ObExprBaseLRpad(alloc, T_FUN_SYS_RPAD, N_RPAD, TWO_OR_THREE)
{}
ObExprOracleRpad::~ObExprOracleRpad()
{}
int ObExprOracleRpad::calc_result_typeN(
ObExprResType& type, ObExprResType* types_array, int64_t param_num, ObExprTypeCtx& type_ctx) const
{
int ret = OB_SUCCESS;
if (param_num == 3) {
if (OB_ISNULL(types_array) || OB_ISNULL(types_array + 1) || OB_ISNULL(types_array + 2)) {
LOG_WARN("NULL param", K(ret), K(types_array[0]), K(types_array[1]), K(types_array[2]));
} else if (OB_FAIL(ObExprBaseLRpad::calc_type(type, types_array[0], types_array[1], &(types_array[2]), type_ctx))) {
LOG_WARN("Failed to calc_type", K(ret));
}
} else if (param_num == 2) {
if (OB_ISNULL(types_array) || OB_ISNULL(types_array + 1)) {
LOG_WARN("NULL param", K(ret), K(types_array[0]), K(types_array[1]));
} else if (OB_FAIL(ObExprBaseLRpad::calc_type(type, types_array[0], types_array[1], NULL, type_ctx))) {
LOG_WARN("Failed to calc_type", K(ret));
}
} else {
ret = OB_NOT_SUPPORTED;
LOG_WARN("Wrong param num", K(ret), K(param_num));
}
return ret;
}
int ObExprOracleRpad::calc_resultN(ObObj& result, const ObObj* objs_array, int64_t param_num, ObExprCtx& expr_ctx) const
{
int ret = OB_SUCCESS;
if (param_num == 3) {
if (OB_ISNULL(objs_array) || OB_ISNULL(objs_array + 1) || OB_ISNULL(objs_array + 2)) {
LOG_WARN("NULL param", K(ret), K(objs_array[0]), K(objs_array[1]), K(objs_array[2]));
} else if (OB_FAIL(
ObExprBaseLRpad::calc(RPAD_TYPE, objs_array[0], objs_array[1], objs_array[2], expr_ctx, result))) {
LOG_WARN("Failed to calc", K(ret));
}
} else if (param_num == 2) {
ObObj pad_text;
pad_text.set_string(ObVarcharType, " ", 1L);
pad_text.set_collation_type(ObCharset::get_system_collation());
if (OB_ISNULL(objs_array) || OB_ISNULL(objs_array + 1)) {
LOG_WARN("NULL param", K(ret), K(objs_array[0]), K(objs_array[1]));
} else if (OB_FAIL(convert_result_collation(result_type_, pad_text, expr_ctx.calc_buf_))) {
LOG_WARN("convert result collation failed", K(ret));
} else if (OB_FAIL(ObExprBaseLRpad::calc(RPAD_TYPE, objs_array[0], objs_array[1], pad_text, expr_ctx, result))) {
LOG_WARN("Failed to calc", K(ret));
}
} else {
ret = OB_NOT_SUPPORTED;
LOG_WARN("Wrong param num", K(ret), K(param_num));
}
return ret;
}
int ObExprOracleRpad::cg_expr(ObExprCGCtx& expr_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
int ret = OB_SUCCESS;
UNUSED(expr_cg_ctx);
UNUSED(raw_expr);
rt_expr.eval_func_ = calc_oracle_rpad_expr;
return ret;
}
int ObExprOracleRpad::calc_oracle_rpad_expr(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res)
{
return calc_oracle_pad_expr(expr, ctx, RPAD_TYPE, res);
}
/* ObExprRpadOracle END }}} */
} // namespace sql
} // namespace oceanbase