/** * 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 #include #include #include #include #include "sql/engine/expr/ob_expr_uuid.h" using namespace oceanbase::common; using namespace oceanbase::sql; namespace oceanbase { namespace sql { int ObBigEndian::put_uint16(unsigned char* b, uint16_t v) { int ret = OB_SUCCESS; if (OB_ISNULL(b)) { ret = OB_ERR_UNEXPECTED; LOG_ERROR("unexpected error. pointer is null", K(ret), K(b)); } else { b[0] = static_cast(v >> 8); b[1] = static_cast(v); } return ret; } int ObBigEndian::put_uint32(unsigned char* b, uint32_t v) { int ret = OB_SUCCESS; if (OB_ISNULL(b)) { ret = OB_ERR_UNEXPECTED; LOG_ERROR("unexpected error. pointer is null", K(ret), K(b)); } else { b[0] = static_cast(v >> 24); b[1] = static_cast(v >> 16); b[2] = static_cast(v >> 8); b[3] = static_cast(v); } return ret; } ObUUIDNode::ObUUIDNode() { is_inited_ = false; (void)init(); } /* * get and set mac address * */ int ObUUIDNode::init() { int ret = OB_SUCCESS; struct ifreq ifr; struct ifconf ifc; char buf[1024]; is_inited_ = false; int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock == -1) { ret = OB_ERR_UNEXPECTED; LOG_ERROR("socket failed", K(ret), K(errno)); } else { ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected error. ioctl failed", K(ret), K(errno)); } else { ifreq* it = ifc.ifc_req; ifreq* end = it + (ifc.ifc_len / sizeof(ifreq)); bool mac_addr_found = false; for (; it != end && OB_SUCC(ret); ++it) { if (OB_ISNULL(it) || OB_ISNULL(it->ifr_name) || OB_ISNULL(ifr.ifr_name)) { continue; } else { strncpy(ifr.ifr_name, it->ifr_name, IFNAMSIZ - 1); if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { if (!(ifr.ifr_flags & IFF_LOOPBACK)) { if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { MEMCPY(mac_addr_, ifr.ifr_hwaddr.sa_data, 6); mac_addr_found = true; break; } } } } } // end for if (OB_FAIL(ret)) { } else if (!mac_addr_found) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected error. can not get mac address", K(ret), K(errno)); } else { is_inited_ = true; } } close(sock); } return ret; } int ObUUIDTime::get_time(uint64_t& time, uint16_t& seq) { int ret = OB_SUCCESS; ObLatchWGuard guard(lock_, ObLatchIds::DEFAULT_MUTEX); uint64_t now = 0; if (OB_FAIL(time_now(now))) { LOG_WARN("get time failed", K(ret)); } else { if (clock_seq_ == 0) { reset_clock_seq(); } now = (now / uint64_t(100)) + G1582NS100; if (now <= lasttime_) { clock_seq_ = static_cast(((clock_seq_ + 1) & 0x3fff) | 0x8000); } lasttime_ = now; time = now; seq = clock_seq_; } return ret; } int ObUUIDTime::time_now(uint64_t& now) { int ret = OB_SUCCESS; struct timespec ts; int tmpret = clock_gettime(CLOCK_REALTIME, &ts); if (tmpret != 0) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get clock time failed", K(ret), K(errno)); } else { now = uint64_t(ts.tv_sec) * uint64_t(1000000000) + uint64_t(ts.tv_nsec); } return ret; } void ObUUIDTime::reset_clock_seq() { uint8_t b[2] = {0, 0}; srandom(static_cast(time(NULL))); b[0] = static_cast(random()); b[1] = static_cast(random()); uint16_t seq = static_cast(uint16_t(b[0] << 8) | uint16_t(b[1])); clock_seq_ = static_cast((seq & 0x3fff) | 0x8000); } ObExprUuid::ObExprUuid(ObIAllocator& alloc) : ObFuncExprOperator(alloc, T_FUN_SYS_UUID, N_UUID, 0, NOT_ROW_DIMENSION) {} ObExprUuid::ObExprUuid( ObIAllocator& alloc, ObExprOperatorType type, const char* name, int32_t param_num, int32_t dimension) : ObFuncExprOperator(alloc, type, name, param_num, dimension) {} ObExprUuid::~ObExprUuid() {} int ObExprUuid::calc_result0(ObObj& result, ObExprCtx& expr_ctx) const { int ret = OB_SUCCESS; char* buffer = NULL; unsigned char scratch[ObExprUuid::LENGTH_UUID]; if (OB_ISNULL(expr_ctx.calc_buf_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("allocator is null", K(ret), K(expr_ctx.calc_buf_)); } else if (OB_UNLIKELY(NULL == (buffer = static_cast(expr_ctx.calc_buf_->alloc(ObExprUuid::LENGTH_UUID))))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("allocate memory failed", K(ret)); } else if (OB_FAIL(calc(scratch))) { LOG_WARN("calc failed", K(ret)); } else { // to string char* start = buffer; for (int64_t i = 0; i < 16; ++i) { if (4 == i || 6 == i || 8 == i || 10 == i) { sprintf(buffer++, "-"); } sprintf(buffer, "%02x", scratch[i]); buffer += 2; } result.set_varchar(start, ObExprUuid::LENGTH_UUID); result.set_collation(result_type_); } return ret; } int ObExprUuid::init() { // will be inited once when and only when server starts // so, please do not worry too much about the contention even we use ob_malloc here. int ret = OB_SUCCESS; ObMemAttr mem_attr; mem_attr.label_ = ObModIds::OB_SQL_EXPR; uuid_node = static_cast(ob_malloc(sizeof(ObUUIDNode), mem_attr)); if (OB_UNLIKELY(NULL == uuid_node)) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("allocate memory failed", K(ret)); } else if (OB_FAIL(uuid_node->init())) { LOG_WARN("init uuid node failed", K(ret)); ob_free(uuid_node); // very important ! do not forget this ! uuid_node = NULL; // very important ! do not forget this ! } return ret; } int ObExprUuid::calc(unsigned char* scratch) { int ret = OB_SUCCESS; if (OB_ISNULL(scratch)) { ret = OB_ERR_UNEXPECTED; LOG_ERROR("unexpected error. pointer is null", K(ret), K(scratch)); } else if (OB_ISNULL(uuid_node) || OB_UNLIKELY(!(uuid_node->is_inited_))) { ret = OB_ERR_UNEXPECTED; LOG_ERROR("unexpected error. uuid node is not inited", K(ret), K(uuid_node)); } else { unsigned char* mac_addr = uuid_node->mac_addr_; uint64_t time = 0; uint16_t seq = 0; if (OB_FAIL(ObUUIDTime::get_time(time, seq))) { LOG_WARN("get time failed", K(ret)); } else { uint32_t time_low = static_cast(time); uint16_t time_mid = static_cast(time >> 32); uint16_t time_hi = static_cast((time >> 48) & 0x0fff); time_hi |= 0x1000; /*faint. really ugly. 4 if(ob_fail)s here, not efficient at all*/ /*may I omit the tests for NULL on scratch ? */ if (OB_FAIL(ObBigEndian::put_uint32(scratch, time_low))) { LOG_WARN("put uint32 failed", K(ret)); } else if (OB_FAIL(ObBigEndian::put_uint16(scratch + 4, time_mid))) { LOG_WARN("put uint16 failed", K(ret)); } else if (OB_FAIL(ObBigEndian::put_uint16(scratch + 6, time_hi))) { LOG_WARN("put uint16 failed", K(ret)); } else if (OB_FAIL(ObBigEndian::put_uint16(scratch + 8, seq))) { LOG_WARN("put uint16 failed", K(ret)); } else { MEMCPY(scratch + 10, mac_addr, 6); } } } return ret; } uint64_t ObUUIDTime::lasttime_ = 0; uint16_t ObUUIDTime::clock_seq_ = 0; ObLatch ObUUIDTime::lock_; ObUUIDNode* ObExprUuid::uuid_node = NULL; int ObExprUuid::eval_uuid(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum) { int ret = OB_SUCCESS; UNUSED(expr); unsigned char scratch[ObExprUuid::LENGTH_UUID] = {0}; if (OB_FAIL(calc(scratch))) { LOG_WARN("calc failed", K(ret)); } else { char* buf = expr.get_str_res_mem(ctx, LENGTH_UUID); int64_t pos = 0; if (OB_ISNULL(buf)) { ret = OB_ERR_UNEXPECTED; SERVER_LOG(WARN, "buff is null", K(ret)); } else { // to string const char* HEXCHARS = "0123456789abcdef"; for (int64_t i = 0; pos < LENGTH_UUID && i < 16; ++i) { if (4 == i || 6 == i || 8 == i || 10 == i) { buf[pos++] = '-'; } buf[pos++] = HEXCHARS[scratch[i] >> 4 & 0xF]; buf[pos++] = HEXCHARS[scratch[i] & 0xF]; } if (OB_SUCC(ret)) { expr_datum.set_string(buf, LENGTH_UUID); } } } return ret; } int ObExprUuid::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const { UNUSED(raw_expr); UNUSED(op_cg_ctx); rt_expr.eval_func_ = ObExprUuid::eval_uuid; return OB_SUCCESS; } ObExprSysGuid::ObExprSysGuid(ObIAllocator& alloc) : ObExprUuid(alloc, T_FUN_SYS_GUID, N_SYS_GUID, 0, NOT_ROW_DIMENSION) {} ObExprSysGuid::~ObExprSysGuid() {} int ObExprSysGuid::calc_result0(ObObj& result, ObExprCtx& expr_ctx) const { int ret = OB_SUCCESS; unsigned char* buffer = NULL; CK(OB_NOT_NULL(expr_ctx.calc_buf_)); if (OB_SUCC(ret) && OB_ISNULL(buffer = static_cast(expr_ctx.calc_buf_->alloc(ObExprUuid::LENGTH_UUID)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("allocate memory for sys guid failed", K(ret)); } OZ(calc(buffer)); OX(result.set_raw(reinterpret_cast(buffer), ObExprSysGuid::LENGTH_SYS_GUID)); OX(result.set_collation(result_type_)); return ret; } int ObExprSysGuid::eval_sys_guid(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum) { int ret = OB_SUCCESS; unsigned char* buf = reinterpret_cast(expr.get_str_res_mem(ctx, LENGTH_UUID)); if (OB_ISNULL(buf)) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("buff is null", K(ret)); } else if (OB_FAIL(calc(buf))) { LOG_WARN("calc uuid failed", K(ret)); } else { expr_datum.set_string(reinterpret_cast(buf), LENGTH_SYS_GUID); } return ret; } } // namespace sql } // namespace oceanbase