Files
oceanbase/src/sql/engine/expr/ob_expr_time_format.cpp
wangzelin.wzl 93a1074b0c patch 4.0
2022-10-24 17:57:12 +08:00

263 lines
9.7 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 "lib/ob_name_def.h"
#include "sql/engine/expr/ob_expr_time_format.h"
#include "sql/session/ob_sql_session_info.h"
#include "sql/engine/expr/ob_datum_cast.h"
#include "sql/engine/ob_exec_context.h"
#include "sql/engine/expr/ob_expr_util.h"
namespace oceanbase
{
using namespace common;
namespace sql
{
ObExprTimeFormat::ObExprTimeFormat(ObIAllocator &alloc)
: ObStringExprOperator(alloc, T_FUN_SYS_TIME_FORMAT, N_TIME_FORMAT, 2)
{
}
ObExprTimeFormat::~ObExprTimeFormat()
{
}
int ObExprTimeFormat::calc_result_type2(ObExprResType &type,
ObExprResType &time,
ObExprResType &format,
common::ObExprTypeCtx &type_ctx) const
{
int ret = common::OB_SUCCESS;
calc_temporal_format_result_length(type, format);
type.set_collation_type(type_ctx.get_coll_type());
type.set_collation_level(common::CS_LEVEL_COERCIBLE);
time.set_calc_type(ObTimeType);
format.set_calc_type(ObVarcharType);
format.set_calc_collation_type(type.get_collation_type());
// result is null if cast first param to ObTimeType failed.
type_ctx.set_cast_mode(type_ctx.get_cast_mode() | CM_NULL_ON_WARN);
return ret;
}
int ObExprTimeFormat::time_to_str_format(const int64_t &time_value, const ObString &format,
char *buf, int64_t buf_len, int64_t &pos, bool &res_null)
{
int ret = OB_SUCCESS;
ObTime ob_time;
if (ObTimeConverter::time_to_ob_time(time_value, ob_time)) {
LOG_WARN("time to ob time failed", K(ret), K(time_value));
} else if (OB_ISNULL(format.ptr()) || OB_ISNULL(buf)
|| OB_UNLIKELY(format.length() <= 0 || buf_len <= 0)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("format or output string is invalid", K(ret), K(format), K(buf), K(buf_len));
} else if (IS_NEG_TIME(ob_time.mode_) && OB_FAIL(databuff_printf(buf, buf_len, pos, "-"))) {
LOG_WARN("print - failed", K(ret));
} else {
const char *format_ptr = format.ptr();
const char *end_ptr = format.ptr() + format.length();
const int32_t *parts = ob_time.parts_;
//used for am/pm conversation in order to avoid if-else tests.
int hour12 = ((parts[DT_HOUR] + 11) % 12) + 1;
while (format_ptr < end_ptr && OB_SUCCESS == ret && !res_null) {
if ('%' == *format_ptr) {
format_ptr++;
if (format_ptr >= end_ptr) {
if (pos >= buf_len) {
ret = OB_SIZE_OVERFLOW;
break;
}
buf[pos++] = '%';
break;
}
switch (*format_ptr) {
case 'f': { //Microseconds (000000..999999), include '-' if value is negative.
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 6, parts[DT_USEC]);
break;
}
case 'h': //Hour (01..12)
case 'I': { //Hour (01..12)
int hour = hour12;
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 2, hour);
break;
}
case 'i': { //Minutes, numeric (00..59)
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 2, parts[DT_MIN]);
break;
}
case 'H': { //Hour
if (parts[DT_HOUR] < 100) {
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 2, parts[DT_HOUR]);
} else {
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 3, parts[DT_HOUR]);
}
break;
}
case 'k': { //Hour
ret = ObTimeConverter::data_fmt_d(buf, buf_len, pos, parts[DT_HOUR]);
break;
}
case 'l': { //Hour (1..12)
int hour = hour12;
ret = ObTimeConverter::data_fmt_d(buf, buf_len, pos, hour);
break;
}
case 'p': { //AM or PM
const char *ptr = (parts[DT_HOUR] % 24) < 12 ? "AM" : "PM";
ret = ObTimeConverter::data_fmt_s(buf, buf_len, pos, ptr);
break;
}
case 'r': { //Time, 12-hour (hh:mm:ss followed by AM or PM)
const char *ptr = (parts[DT_HOUR] % 24) < 12 ? "AM" : "PM";
ret = databuff_printf(buf, buf_len, pos, "%02d:%02d:%02d %s", hour12, parts[DT_MIN], parts[DT_SEC], ptr);
break;
}
case 'S': //Seconds (00..59)
case 's': { //Seconds (00..59)
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 2, parts[DT_SEC]);
break;
}
case 'T': { //Time (hh:mm:ss)
if (parts[DT_HOUR] < 100) {
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 2, parts[DT_HOUR]);
} else {
ret = ObTimeConverter::data_fmt_nd(buf, buf_len, pos, 3, parts[DT_HOUR]);
}
if (OB_SUCC(ret)) {
ret = databuff_printf(buf, buf_len, pos, ":%02d:%02d", parts[DT_MIN], parts[DT_SEC]);
}
break;
}
case 'c': //Month, numeric (0..12)
case 'e': { //Day of the month, numeric (0..31)
ret = databuff_printf(buf, buf_len, pos, "0");
break;
}
case 'd': //Day of the month, numeric (00..31)
case 'm': //Month, numeric (00..12)
case 'y': { //Year, numeric (two digits)
ret = databuff_printf(buf, buf_len, pos, "00");
break;
}
case 'Y': { //Year, numeric, four digits
ret = databuff_printf(buf, buf_len, pos, "0000");
break;
}
case 'a': //Abbreviated weekday name (Sun..Sat)
case 'b': //Abbreviated month name (Jan..Dec)
case 'D': //Day of the month with English suffix (0th, 1st, 2nd, 3rd...)
case 'j': //Day of year (001..366)
case 'M': //Month name (January..December)
case 'U': //Week (00..53), where Sunday is the first day of the week
case 'u': //Week (00..53), where Monday is the first day of the week
case 'V': //Week (01..53), where Sunday is the first day of the week; used with %X
case 'v': //Week (01..53), where Monday is the first day of the week; used with %x
case 'W': //Weekday name (Sunday..Saturday)
case 'w': //Day of the week (0=Sunday..6=Saturday)
case 'X': //Year for the week where Sunday is the first day of the week
case 'x': { //Year for the week, where Monday is the first day of the week
res_null = true;
break;
}
case '%': { //A literal "%" character
if (pos >= buf_len) {
ret = OB_SIZE_OVERFLOW;
break;
}
buf[pos++] = '%';
break;
}
default: {
if (pos >= buf_len) {
ret = OB_SIZE_OVERFLOW;
break;
}
buf[pos++] = *format_ptr;
break;
}
}
if (OB_SUCC(ret)) {
format_ptr++;
} else {
LOG_WARN("print failed", K(ret), K(*format_ptr), K(time_value), K(ob_time));
}
} else if (pos >= buf_len) {
ret = OB_SIZE_OVERFLOW;
break;
} else {
buf[pos++] = *(format_ptr++);
}
}
}
return ret;
}
int ObExprTimeFormat::cg_expr(ObExprCGCtx &op_cg_ctx,
const ObRawExpr &raw_expr,
ObExpr &rt_expr) const
{
UNUSED(op_cg_ctx);
UNUSED(raw_expr);
int ret = OB_SUCCESS;
if (rt_expr.arg_cnt_ != 2) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("date_format expr should have two params", K(ret), K(rt_expr.arg_cnt_));
} else if (OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0])
|| OB_ISNULL(rt_expr.args_[1])) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("children of date_format expr is null", K(ret), K(rt_expr.args_));
} else {
rt_expr.eval_func_ = ObExprTimeFormat::calc_time_format;
}
return ret;
}
int ObExprTimeFormat::calc_time_format(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &expr_datum)
{
int ret = OB_SUCCESS;
char *buf = NULL;
int64_t buf_len = 0;
int64_t pos = 0;
ObDatum *time = NULL;
ObDatum *format = NULL;
bool res_null = false;
if (OB_FAIL(expr.eval_param_value(ctx, time, format))) {
LOG_WARN("calc param failed", K(ret));
} else if (time->is_null() || format->is_null()) {
expr_datum.set_null();
} else if (OB_UNLIKELY(format->get_string().empty())) {
expr_datum.set_null();
} else if (FALSE_IT(buf_len = format->get_string().length() * OB_TEMPORAL_BUF_SIZE_RATIO)) {
} else if (OB_ISNULL(buf = expr.get_str_res_mem(ctx, buf_len))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("no more memory to alloc for buf", K(ret), K(buf_len));
} else if (OB_FAIL(time_to_str_format(time->get_time(),
format->get_string(),
buf,
buf_len,
pos,
res_null))) {
LOG_WARN("failed to convert ob time to str with format");
} else if (res_null) {
expr_datum.set_null();
} else {
expr_datum.set_string(buf, static_cast<int32_t>(pos));
}
return ret;
}
}
}