Files
oceanbase/src/sql/engine/expr/ob_expr_update_xml.cpp
2023-09-20 17:01:21 +00:00

958 lines
39 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.
* This file is for func updatexml.
*/
#include "ob_expr_update_xml.h"
#ifdef OB_BUILD_ORACLE_XML
#include "sql/engine/expr/ob_expr_xml_func_helper.h"
#include "lib/xml/ob_xml_parser.h"
#include "lib/xml/ob_xml_util.h"
#endif
#include "sql/session/ob_sql_session_info.h"
#include "sql/engine/ob_exec_context.h"
#include "sql/session/ob_sql_session_info.h"
#include "sql/engine/ob_exec_context.h"
#define USING_LOG_PREFIX SQL_ENG
using namespace oceanbase::common;
namespace oceanbase
{
namespace sql
{
ObExprUpdateXml::ObExprUpdateXml(common::ObIAllocator &alloc)
: ObFuncExprOperator(alloc, T_FUN_SYS_UPDATE_XML, N_UPDATEXML, MORE_THAN_TWO, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION)
{
}
ObExprUpdateXml::~ObExprUpdateXml() {}
int ObExprUpdateXml::calc_result_typeN(ObExprResType &type,
ObExprResType *types,
int64_t param_num,
common::ObExprTypeCtx &type_ctx) const
{
int ret = OB_SUCCESS;
if (param_num < 3) {
ret = OB_ERR_PARAM_SIZE;
LOG_WARN("invalid param number", K(ret), K(param_num));
} else if (!is_called_in_sql()) {
ret = OB_ERR_SP_LILABEL_MISMATCH;
LOG_WARN("expr call in pl semantics disallowed", K(ret), K(N_UPDATEXML));
LOG_USER_ERROR(OB_ERR_SP_LILABEL_MISMATCH, static_cast<int>(strlen(N_UPDATEXML)), N_UPDATEXML);
} else if (types[0].is_ext() && types[0].get_udt_id() == T_OBJ_XML) {
types[0].get_calc_meta().set_sql_udt(ObXMLSqlType);
} else if (!ob_is_xml_sql_type(types[0].get_type(), types[0].get_subschema_id())) {
ret = OB_ERR_INVALID_TYPE_FOR_OP;
LOG_USER_ERROR(OB_ERR_INVALID_TYPE_FOR_OP, "-", "-");
LOG_WARN("inconsistent datatypes", K(ret), K(ob_obj_type_str(types[0].get_type())));
}
if (OB_FAIL(ret)) {
} else {
bool has_ns_str = (param_num - 1) % 2 == 1;
int64_t xpath_value_end = has_ns_str ? param_num - 1 : param_num;
for (int64_t i = 1; i < xpath_value_end && OB_SUCC(ret); i++) {
ObObjType param_type = types[i].get_type();
ObCollationType cs_type = types[i].get_collation_type();
if (i % 2 == 1) {
// xpath string
if (param_type == ObNullType) {
} else if (ob_is_string_type(param_type)) {
if (types[i].get_charset_type() != CHARSET_UTF8MB4) {
types[i].set_calc_collation_type(CS_TYPE_UTF8MB4_BIN);
}
}
} else {
// value expr
if (param_type == ObNullType || ob_is_xml_sql_type(param_type, types[i].get_subschema_id())) {
} else if (types[i].is_ext() && types[i].get_udt_id() == T_OBJ_XML) {
types[i].get_calc_meta().set_sql_udt(ObXMLSqlType);
} else if (ob_is_clob(param_type, cs_type) || ob_is_blob(param_type, cs_type)) {
types[i].set_calc_collation_type(CS_TYPE_UTF8MB4_BIN);
} else {
types[i].set_calc_type(ObVarcharType);
types[i].set_calc_collation_type(CS_TYPE_UTF8MB4_BIN);
}
}
}
if (has_ns_str) {
ObObjType param_type = types[param_num - 1].get_type();
if (param_type == ObNullType) {
} else if (ob_is_string_type(param_type)) {
if (types[param_num - 1].get_charset_type() != CHARSET_UTF8MB4) {
types[param_num - 1].set_calc_collation_type(CS_TYPE_UTF8MB4_BIN);
}
}
}
}
if (OB_SUCC(ret)) {
type.set_sql_udt(ObXMLSqlType);
}
return ret;
}
#ifdef OB_BUILD_ORACLE_XML
int ObExprUpdateXml::eval_update_xml(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res)
{
int ret = OB_SUCCESS;
ObEvalCtx::TempAllocGuard tmp_alloc_g(ctx);
common::ObArenaAllocator &allocator = tmp_alloc_g.get_allocator();
ObDatum *xml_datum = NULL;
ObString namespace_str;
ObIMulModeBase *xml_tree = NULL;
bool has_namespace_str = false;
int64_t num_child = expr.arg_cnt_;
ObPathVarObject prefix_ns(allocator);
ObString default_ns;
ObCollationType cs_type = CS_TYPE_INVALID;
ObMulModeMemCtx* xml_mem_ctx = nullptr;
bool input_is_doc = false;
ObMulModeNodeType node_type = M_MAX_TYPE;
lib::ObMallocHookAttrGuard malloc_guard(lib::ObMemAttr(ObXMLExprHelper::get_tenant_id(ctx.exec_ctx_.get_my_session()), "XMLModule"));
if (OB_ISNULL(ctx.exec_ctx_.get_my_session())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get session failed.", K(ret));
} else if (OB_FAIL(ObXmlUtil::create_mulmode_tree_context(&allocator, xml_mem_ctx))) {
LOG_WARN("fail to create tree memory context", K(ret));
} else if (num_child < 3) {
ret = OB_ERR_PARAM_SIZE;
LOG_WARN("invalid param number", K(ret), K(num_child));
} else if (OB_FAIL(ObXMLExprHelper::get_xmltype_from_expr(expr.args_[0], ctx, xml_datum))) {
LOG_WARN("fail to get xmltype", K(ret));
} else if (FALSE_IT(has_namespace_str = ((num_child - 1) % 2 != 0))) {
} else if (has_namespace_str) {
// namespace can be NULL
if (ObNullType == expr.args_[num_child - 1]->datum_meta_.type_) {
} else if (!ob_is_string_type(expr.args_[num_child - 1]->datum_meta_.type_)) {
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
} else if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr.args_[num_child - 1], ctx, namespace_str, allocator))) {
LOG_WARN("fail to get namespace string", K(ret));
} else if (OB_FAIL(ObXMLExprHelper::construct_namespace_params(namespace_str, default_ns, prefix_ns, allocator))) {
LOG_WARN("fail to construct namespace params", K(ret), K(namespace_str));
}
}
if (OB_SUCC(ret)) {
int64_t xpath_value_size = has_namespace_str ? num_child - 1 : num_child;
if (OB_FAIL(ObXMLExprHelper::get_xml_base(xml_mem_ctx, xml_datum, cs_type, ObNodeMemType::TREE_TYPE, xml_tree, node_type, true))) {
LOG_WARN("fail to get xml base", K(ret));
}
// do update xml
for (int64_t i = 1; i < xpath_value_size && OB_SUCC(ret); i+=2) {
ObString xpath_str;
if (ObNullType == expr.args_[i]->datum_meta_.type_) {
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
LOG_WARN("invalid xpath expression", K(ret));
} else if (!ob_is_string_type(expr.args_[i]->datum_meta_.type_)) {
} else if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr.args_[i], ctx, xpath_str, allocator))) {
LOG_WARN("fail to get xpath string", K(ret), K(i));
} else if (xpath_str.empty()) {
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
LOG_WARN("xpath is empty", K(ret));
} else if (OB_FAIL(update_xml_tree(xml_mem_ctx, expr.args_[i+1], ctx, xpath_str, default_ns, &prefix_ns, xml_tree))) {
LOG_WARN("fail to do update in xml tree", K(ret), K(xml_tree), K(xpath_str), K(default_ns), K(i+1));
}
}
// set result
if (OB_SUCC(ret)) {
ObXmlDocument *xml_doc = static_cast<ObXmlDocument *>(xml_tree);
ObStringBuffer buff(&allocator);
ObString xml_plain_text;
if (OB_ISNULL(xml_doc)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("allocate xml doc failed", K(ret));
} else if (OB_FAIL(xml_doc->print_document(buff, CS_TYPE_INVALID, ObXmlFormatType::NO_FORMAT))) {
LOG_WARN("fail to print xml tree", K(ret));
} else if (FALSE_IT(xml_plain_text.assign_ptr(buff.ptr(), buff.length()))) {
} else if (node_type == ObMulModeNodeType::M_DOCUMENT &&
OB_FAIL(ObXmlParserUtils::parse_document_text(xml_mem_ctx, xml_plain_text, xml_doc))) {
if (ret == OB_ERR_PARSER_SYNTAX) ret = OB_ERR_XML_PARSE;
LOG_WARN("parsing document failed", K(ret), K(xml_plain_text));
} else if (node_type != ObMulModeNodeType::M_DOCUMENT && OB_FAIL(ObXmlParserUtils::parse_content_text(xml_mem_ctx, xml_plain_text, xml_doc))) {
if (ret == OB_ERR_PARSER_SYNTAX) ret = OB_ERR_XML_PARSE;
LOG_WARN("parsing content failed", K(ret), K(xml_plain_text));
}
if (OB_FAIL(ret)) {
} else if (xml_doc->count() == 0) {
res.set_null();
} else if (OB_FAIL(ObXMLExprHelper::pack_xml_res(expr, ctx, res, xml_doc,
xml_mem_ctx,
node_type == ObMulModeNodeType::M_DOCUMENT ?
ObMulModeNodeType::M_DOCUMENT : ObMulModeNodeType::M_CONTENT,
xml_plain_text))) {
LOG_WARN("fail to pack xml res", K(ret), K(xml_doc), K(xml_plain_text));
}
}
}
return ret;
}
int ObExprUpdateXml::update_xml_tree(ObMulModeMemCtx* xml_mem_ctx,
const ObExpr *expr,
ObEvalCtx &ctx,
ObString &xpath_str,
ObString &default_ns,
ObPathVarObject *prefix_ns,
ObIMulModeBase *xml_tree)
{
int ret = OB_SUCCESS;
ObPathExprIter xpath_iter((static_cast<ObXmlNode*>(xml_tree))->get_mem_ctx()->allocator_);
ObIMulModeBase *node = NULL;
if (OB_FAIL(xpath_iter.init((static_cast<ObXmlNode*>(xml_tree))->get_mem_ctx(), xpath_str, default_ns, xml_tree, prefix_ns))) {
LOG_WARN("fail to init xpath iterator", K(xpath_str), K(default_ns), K(ret));
ObXMLExprHelper::replace_xpath_ret_code(ret);
} else if (OB_FAIL(xpath_iter.open())) {
LOG_WARN("fail to open xpath iterator", K(ret));
ObXMLExprHelper::replace_xpath_ret_code(ret);
}
ObArray<ObIMulModeBase*> res_array;
while (OB_SUCC(ret)) {
if (OB_FAIL(xpath_iter.get_next_node(node))) {
if (ret != OB_ITER_END) {
LOG_WARN("fail to get next xml node", K(ret));
}
} else if (OB_FAIL(res_array.push_back(node))) {
LOG_WARN("fail to push xml node", K(ret));
}
}
if (ret == OB_ITER_END) {
ret = OB_SUCCESS;
}
for (int i = 0; i < res_array.size() && OB_SUCC(ret); ++i) {
ObIMulModeBase* update_node = res_array[i];
if (OB_ISNULL(update_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("xpath result node is null", K(ret));
} else if (OB_FAIL(update_xml_node(xml_mem_ctx, expr, ctx, update_node))) {
LOG_WARN("fail to update xml node", K(ret), K(node));
}
}
int tmp_ret = OB_SUCCESS;
if (OB_SUCCESS != (tmp_ret = xpath_iter.close())) {
LOG_WARN("fail to close xpath iter", K(tmp_ret));
ret = COVER_SUCC(tmp_ret);
}
return ret;
}
int ObExprUpdateXml::update_xml_node(ObMulModeMemCtx* xml_mem_ctx,
const ObExpr *expr,
ObEvalCtx &ctx,
ObIMulModeBase *node)
{
int ret = OB_SUCCESS;
ObXmlNode *xml_node = static_cast<ObXmlNode *>(node);
ObMulModeNodeType xml_type = xml_node->type();
bool is_empty_content = false;
if (OB_ISNULL(xml_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret));
} else if (xml_type == M_DOCUMENT) {
int64_t child_size = xml_node->size();
bool is_found = false;
for (int64_t i = 0; OB_SUCC(ret) && !is_found && i < child_size ; i++) {
ObXmlNode *child_node = xml_node->at(i);
if (OB_ISNULL(child_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("child node is NULL", K(ret));
} else if (child_node->type() == M_ELEMENT) {
xml_node = child_node;
is_found = true;
}
}
if (!is_found) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("fail to found the root element node", K(ret));
}
} else if (xml_type == M_CONTENT) {
if (xml_node->size() > 0) {
xml_node = xml_node->at(xml_node->size() - 1);
} else {
is_empty_content = true;
}
}
if (!is_empty_content) {
switch (xml_node->type()) {
case M_TEXT: {
if (OB_FAIL(update_text_or_attribute_node(xml_mem_ctx, xml_node, expr, ctx, true))) {
LOG_WARN("fail to update text node", K(ret), K(xml_type));
}
break;
}
case M_ATTRIBUTE: {
if (OB_FAIL(update_text_or_attribute_node(xml_mem_ctx, xml_node, expr, ctx, false))) {
LOG_WARN("fail to update attribute node", K(ret));
}
break;
}
case M_NAMESPACE: {
if (OB_FAIL(update_namespace_node(xml_mem_ctx, xml_node, expr, ctx))) {
LOG_WARN("fail to update namespace node", K(ret));
}
break;
}
case M_COMMENT:
case M_CDATA: {
if (OB_FAIL(update_cdata_and_comment_node(xml_mem_ctx, xml_node, expr, ctx))) {
LOG_WARN("fail to update cdata node", K(ret), K(xml_type));
}
break;
}
case M_ELEMENT: {
if (OB_FAIL(update_element_node(xml_mem_ctx, xml_node, expr, ctx))) {
LOG_WARN("fail to update element node", K(ret), K(xml_type));
}
break;
}
case M_INSTRUCT: {
if (OB_FAIL(update_pi_node(xml_mem_ctx, xml_node, expr, ctx))) {
LOG_WARN("fail to pi node", K(ret), K(xml_type));
}
break;
}
default: {
ret = OB_NOT_SUPPORTED;
LOG_WARN("unsupported xml node type", K(ret), K(xml_type));
break;
}
}
}
return ret;
}
int ObExprUpdateXml::update_pi_node(ObMulModeMemCtx* xml_mem_ctx,
ObXmlNode *xml_node,
const ObExpr *expr,
ObEvalCtx &ctx)
{
int ret = OB_SUCCESS;
ObObjType val_type = expr->datum_meta_.type_;
uint16_t sub_schema_id = expr->obj_meta_.get_subschema_id();
ObDatum *datum = NULL;
if (OB_FAIL(expr->eval(ctx, datum))) {
LOG_WARN("fail to eval datum", K(ret));
} else {
if (val_type == ObNullType) {
ret = OB_ERR_UPDATE_XML_WITH_INVALID_NODE;
LOG_WARN("XML nodes must be updated with valid nodes and of the same type", K(ret));
} else if (ob_is_string_type(val_type)) {
ObXmlDocument *xml_doc = NULL;
ObString value_str;
if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr, ctx, value_str, *xml_mem_ctx->allocator_))) {
LOG_WARN("fail to get value str", K(ret), K(value_str));
} else if (OB_FAIL(ObXmlParserUtils::parse_content_text(xml_mem_ctx, value_str, xml_doc))) {
if (ret == OB_ERR_PARSER_SYNTAX) {
ret = OB_ERR_UPDATE_XML_WITH_INVALID_NODE;
}
LOG_WARN("fail to parse xml str", K(ret));
} else if (xml_doc->size() > 0 && xml_doc->at(0)->type() == M_INSTRUCT) {
if (OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, xml_node, xml_doc))) {
LOG_WARN("fail to update pi node with content node", K(ret));
}
} else {
ret = OB_ERR_UPDATE_XML_WITH_INVALID_NODE;
LOG_WARN("update pi node with invalid node", K(ret), K(value_str));
}
} else if (ob_is_xml_sql_type(val_type, sub_schema_id)) {
ObIMulModeBase *update_node = NULL;
ObXmlNode *update_xml_node = NULL;
ObCollationType cs_type = CS_TYPE_INVALID;
if (OB_FAIL(ObXMLExprHelper::get_xml_base(xml_mem_ctx, datum, cs_type, ObNodeMemType::TREE_TYPE, update_node))) {
LOG_WARN("fail to get update xml node", K(ret));
} else if (OB_ISNULL(update_xml_node = static_cast<ObXmlNode *>(update_node))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret));
} else if (update_xml_node->size() == 0) {
ret = OB_ERR_UPDATE_XML_WITH_INVALID_NODE;
LOG_WARN("update pi node with invalid node", K(ret));
} else if (OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, xml_node, update_xml_node))) {
LOG_WARN("fail to update xml content node ", K(ret));
}
}
}
return ret;
}
int ObExprUpdateXml::update_text_or_attribute_node(ObMulModeMemCtx* xml_mem_ctx,
ObXmlNode *xml_node,
const ObExpr *expr,
ObEvalCtx &ctx,
bool is_text)
{
int ret = OB_SUCCESS;
ObObjType val_type = expr->datum_meta_.type_;
ObCollationType cs_type = expr->datum_meta_.cs_type_;
uint16_t sub_schema_id = expr->obj_meta_.get_subschema_id();
ObDatum *datum = NULL;
if (OB_FAIL(expr->eval(ctx, datum))) {
LOG_WARN("fail to eval datum", K(ret));
} else {
if (val_type == ObNullType) {
xml_node->set_value(ObString::make_empty_string());
} else if (is_text && ob_is_clob(val_type, cs_type)) {
ObXmlDocument *xml_doc = NULL;
ObString value_str;
if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr, ctx, value_str, *xml_mem_ctx->allocator_))) {
LOG_WARN("fail to get value str", K(ret), K(value_str));
} else if (value_str.empty()) {
ret = OB_LOB_VALUE_NOT_EXIST;
LOG_WARN("LOB value is empty", K(ret), K(value_str));
} else if (OB_FAIL(ObXmlParserUtils::parse_content_text(xml_mem_ctx, value_str, xml_doc))) {
if (ret == OB_ERR_PARSER_SYNTAX) {
ret = OB_ERR_XML_PARSE;
}
LOG_WARN("fail to parse xml str", K(ret));
} else if (OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, xml_node, xml_doc))) {
LOG_WARN("fail to update xml element node", K(ret));
}
} else if (ob_is_string_type(val_type)) {
ObStringBuffer buff(xml_mem_ctx->allocator_);
ObString value_str;
if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr, ctx, value_str, *xml_mem_ctx->allocator_))) {
LOG_WARN("fail to get value str", K(ret));
} else {
xml_node->set_value(value_str);
}
} else if (ob_is_xml_sql_type(val_type, sub_schema_id)) {
ObIMulModeBase *update_node = NULL;
ObXmlNode *update_xml_node = NULL;
ObCollationType cs_type = CS_TYPE_INVALID;
if (OB_FAIL(ObXMLExprHelper::get_xml_base(xml_mem_ctx, datum, cs_type, ObNodeMemType::TREE_TYPE, update_node))) {
LOG_WARN("fail to get update xml node", K(ret));
} else if (OB_ISNULL(update_xml_node = static_cast<ObXmlNode *>(update_node))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret));
} else if (is_text && OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, xml_node, update_xml_node))) {
LOG_WARN("fail to update xml node ", K(ret));
} else if (!is_text && OB_FAIL(update_attribute_xml_node(xml_node, update_xml_node))) {
LOG_WARN("fail to update xml node ", K(ret));
}
}
}
return ret;
}
int ObExprUpdateXml::update_namespace_node(ObMulModeMemCtx* xml_mem_ctx,
ObXmlNode *xml_node,
const ObExpr *expr,
ObEvalCtx &ctx)
{
int ret = OB_SUCCESS;
ObObjType val_type = expr->datum_meta_.type_;
uint16_t sub_schema_id = expr->obj_meta_.get_subschema_id();
ObDatum *datum = NULL;
if (OB_FAIL(expr->eval(ctx, datum))) {
LOG_WARN("fail to eval datum", K(ret));
} else {
if (val_type == ObNullType) {
ret = OB_ERR_XML_PARSE;
LOG_WARN("update namespace node value to be NULL is unsupported", K(ret));
} else if (ob_is_string_type(val_type)) {
ObStringBuffer buff(xml_mem_ctx->allocator_);
ObString value_str;
if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr, ctx, value_str, *xml_mem_ctx->allocator_))) {
LOG_WARN("fail to get value str", K(ret));
} else if (OB_FAIL(update_namespace_value(*xml_mem_ctx->allocator_, xml_node, value_str))) {
LOG_WARN("fail to update namespace node value", K(ret), K(value_str));
}
} else if (ob_is_xml_sql_type(val_type, sub_schema_id)) {
ObIMulModeBase *update_node = NULL;
ObXmlNode *update_xml_node = NULL;
ObCollationType cs_type = CS_TYPE_INVALID;
if (OB_FAIL(ObXMLExprHelper::get_xml_base(xml_mem_ctx, datum, cs_type, ObNodeMemType::TREE_TYPE, update_node))) {
LOG_WARN("fail to get update xml node", K(ret));
} else if (OB_ISNULL(update_xml_node = static_cast<ObXmlNode *>(update_node))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret));
} else if (OB_FAIL(update_namespace_xml_node(*xml_mem_ctx->allocator_, xml_node, update_xml_node))) {
LOG_WARN("fail to update xml node ", K(ret));
}
}
}
return ret;
}
int ObExprUpdateXml::update_element_node(ObMulModeMemCtx* xml_mem_ctx,
ObXmlNode *xml_node,
const ObExpr *expr,
ObEvalCtx &ctx)
{
int ret = OB_SUCCESS;
ObObjType val_type = expr->datum_meta_.type_;
uint16_t sub_schema_id = expr->obj_meta_.get_subschema_id();
ObDatum *datum = NULL;
if (OB_FAIL(expr->eval(ctx, datum))) {
LOG_WARN("fail to eval datum", K(ret));
} else {
ObXmlElement *ele = static_cast<ObXmlElement *>(xml_node);
if (val_type == ObNullType) {
ObMulModeNodeType node_type = ele->type();
if (OB_FAIL(clear_element_child_node(ele))) {
LOG_WARN("fail to clear child node", K(ret));
}
} else if (ob_is_string_type(val_type)) {
ObXmlDocument *xml_doc = NULL;
ObString value_str;
if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr, ctx, value_str, *xml_mem_ctx->allocator_))) {
LOG_WARN("fail to get value str", K(ret), K(value_str));
} else if (value_str.trim().empty()) {
if (OB_FAIL(clear_element_child_node(ele))) {
LOG_WARN("fail to clear child node", K(ret));
}
} else if (OB_FAIL(ObXmlParserUtils::parse_content_text(xml_mem_ctx, value_str, xml_doc))) {
if (ret == OB_ERR_PARSER_SYNTAX) {
ret = OB_ERR_XML_PARSE;
}
LOG_WARN("fail to parse xml str", K(ret));
} else if (OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, ele, xml_doc))) {
LOG_WARN("fail to update xml element node", K(ret));
}
} else if (ob_is_xml_sql_type(val_type, sub_schema_id)) {
ObIMulModeBase *update_node = NULL;
ObXmlNode *update_xml_node = NULL;
ObCollationType cs_type = CS_TYPE_INVALID;
if (OB_FAIL(ObXMLExprHelper::get_xml_base(xml_mem_ctx, datum, cs_type, ObNodeMemType::TREE_TYPE, update_node))) {
LOG_WARN("fail to get update xml node", K(ret));
} else if (OB_ISNULL(update_xml_node = static_cast<ObXmlNode *>(update_node))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret));
} else if (update_xml_node->count() == 0) {
if (OB_FAIL(clear_element_child_node(ele))) {
LOG_WARN("fail to clear child node", K(ret));
}
} else if (OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, xml_node, update_xml_node))) {
LOG_WARN("fail to update xml node ", K(ret));
}
}
}
return ret;
}
int ObExprUpdateXml::update_cdata_and_comment_node(ObMulModeMemCtx* xml_mem_ctx,
ObXmlNode *xml_node,
const ObExpr *expr,
ObEvalCtx &ctx)
{
int ret = OB_SUCCESS;
ObObjType val_type = expr->datum_meta_.type_;
uint16_t sub_schema_id = expr->obj_meta_.get_subschema_id();
ObDatum *datum = NULL;
if (OB_FAIL(expr->eval(ctx, datum))) {
LOG_WARN("fail to eval datum", K(ret));
} else {
if (val_type == ObNullType) {
xml_node->set_value(ObString::make_empty_string());
} else if (ob_is_string_type(val_type)) {
ObXmlDocument *xml_doc = NULL;
ObString value_str;
if (OB_FAIL(ObXMLExprHelper::get_str_from_expr(expr, ctx, value_str, *xml_mem_ctx->allocator_))) {
LOG_WARN("fail to get value str", K(ret), K(value_str));
} else if (OB_FAIL(ObXmlParserUtils::parse_content_text(xml_mem_ctx, value_str, xml_doc))) {
if (ret == OB_ERR_PARSER_SYNTAX) {
ret = OB_ERR_XML_PARSE;
}
LOG_WARN("fail to parse xml str", K(ret));
} else if (OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, xml_node, xml_doc))) {
LOG_WARN("fail to update xml element node", K(ret));
}
} else if (ob_is_xml_sql_type(val_type, sub_schema_id)) {
ObIMulModeBase *update_node = NULL;
ObXmlNode *update_xml_node = NULL;
ObCollationType cs_type = CS_TYPE_INVALID;
if (OB_FAIL(ObXMLExprHelper::get_xml_base(xml_mem_ctx, datum, cs_type, ObNodeMemType::TREE_TYPE, update_node))) {
LOG_WARN("fail to get update xml node", K(ret));
} else if (OB_ISNULL(update_xml_node = static_cast<ObXmlNode *>(update_node))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret));
} else if (OB_FAIL(update_xml_child_node(*xml_mem_ctx->allocator_, xml_node, update_xml_node))) {
LOG_WARN("fail to update xml node ", K(ret));
}
}
}
return ret;
}
int ObExprUpdateXml::update_namespace_value(ObIAllocator &allocator, ObXmlNode *xml_node, const ObString &ns_value)
{
int ret = OB_SUCCESS;
ObXmlElement *parent = NULL;
ObXmlAttribute *ns = NULL;
ObXmlAttribute *new_ns = NULL;
ObString key;
if (OB_ISNULL(xml_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("xml node is NULL", K(ret));
} else if (xml_node->type() != M_NAMESPACE) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("xml node type is not expected", K(ret), K(xml_node->type()));
} else {
ns = static_cast<ObXmlAttribute *>(xml_node);
parent = static_cast<ObXmlElement *>(xml_node->get_parent());
key = ns->get_key();
}
if (OB_FAIL(ret)) {
} else if (0 == key.compare(ObXmlConstants::XMLNS_STRING)) {
ret = OB_ERR_XML_PARSE;
LOG_WARN("defaul namespace is not allowed to update value", K(ret));
} else if (OB_FAIL(update_exist_nodes_ns(parent, ns))) {
LOG_WARN("fail to udpate exist node ns", K(ret));
} else if (OB_ISNULL(new_ns = OB_NEWx(ObXmlAttribute, (&allocator), ObMulModeNodeType::M_NAMESPACE, parent->get_mem_ctx()))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("alloc failed", K(ret));
} else {
int64_t pos = -1;
new_ns->set_value(ns_value);
new_ns->set_prefix(ObXmlConstants::XMLNS_STRING);
new_ns->set_key(key);
if (OB_FAIL(parent->get_attribute_pos(ObMulModeNodeType::M_NAMESPACE, key, pos))) {
LOG_WARN("fail to get namespace node pos", K(ret));
} else if (OB_FAIL(parent->remove_namespace(pos))) {
LOG_WARN("fail to remove namespace node", K(ret));
} else if (OB_FAIL(parent->add_attribute(new_ns, false, 0))) {
LOG_WARN("fail to add new namespace node", K(ret));
}
}
return ret;
}
int ObExprUpdateXml::clear_element_child_node(ObXmlElement *ele_node)
{
int ret = OB_SUCCESS;
// 1. first clear child node
if (OB_ISNULL(ele_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("xml node is NULL", K(ret));
} else {
// remove all child node
int64_t child_size = ele_node->size();
for (int64_t i = 0; OB_SUCC(ret) && i < child_size; i++) {
if (OB_FAIL(ele_node->remove_element(ele_node->at(0)))) {
LOG_WARN("fail to remove element node", K(ret));
}
}
// remove all attributes and namespaces not use
int64_t attr_size = ele_node->attribute_size(); // attributes size will change after add or remove
for (int64_t i = 0; OB_SUCC(ret) && i < attr_size; i++) {
ObXmlAttribute *attr = NULL;
// use as a queue, always remove the first node
if (OB_FAIL(ele_node->get_attribute(attr, 0))) {
LOG_WARN("fail to get attribute", K(ret), K(i));
} else if (attr->type() == M_NAMESPACE) {
if (OB_FAIL(ele_node->remove_namespace(0))) {
LOG_WARN("fail to remove namespace", K(ret));
}
} else if (OB_FAIL(ele_node->remove_attribute(0))) {
LOG_WARN("fail to remove attribute node", K(ret));
}
}
// add namespace if use
ObXmlAttribute *ns = ele_node->get_ns();
if (OB_SUCC(ret) && OB_NOT_NULL(ns)) {
if (ele_node->get_prefix().empty()) {
ObXmlAttribute *default_ns = NULL;
if (OB_FAIL(get_valid_default_ns_from_parent(ele_node->get_parent(), default_ns))) {
LOG_WARN("fail to get valid default ns", K(ret));
} else if (OB_NOT_NULL(default_ns)
&& default_ns->get_value().compare(ns->get_value()) == 0) {
// do nothing
} else if (OB_FAIL(ele_node->add_attribute(ns, false, 0))) {
LOG_WARN("fail to add default namespace", K(ret));
}
} else if (OB_FAIL(ele_node->add_attribute(ns, false, 0))) {
LOG_WARN("fail to add prefix namespace", K(ret));
}
}
}
return ret;
}
int ObExprUpdateXml::update_attribute_xml_node(ObXmlNode *old_node, ObXmlNode *update_node)
{
int ret = OB_SUCCESS;
int64_t pos = -1;
ObXmlNode *parent = NULL;
ObString key;
ObXmlElement *ele_node = NULL;
if (OB_ISNULL(old_node) || OB_ISNULL(update_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("node is NULL", K(ret), K(old_node), K(update_node));
} else if (OB_ISNULL(parent = old_node->get_parent())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("attribute parent node is NULL", K(ret));
} else if (FALSE_IT(key = old_node->get_key())) {
} else if (parent->type() != M_ELEMENT) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("parent of attribute node is not an element node", K(ret), K(parent->type()));
} else if (FALSE_IT(ele_node = static_cast<ObXmlElement *>(parent))) {
} else if (OB_FAIL(ele_node->get_attribute_pos(old_node->type(), key, pos))) {
LOG_WARN("fail to get attribute pos", K(ret), K(key), K(old_node->type()));
} else if (OB_FAIL(ele_node->remove_attribute(pos))) { // remove attribute
LOG_WARN("fail to remove attribute", K(ret), K(pos), K(key));
} else if (OB_FAIL(remove_and_insert_element_node(ele_node, update_node, 0, false))) {
LOG_WARN("fail to update element node", K(ret));
}
return ret;
}
int ObExprUpdateXml::update_namespace_xml_node(ObIAllocator &allocator, ObXmlNode *old_node, ObXmlNode *update_node)
{
int ret = OB_SUCCESS;
int64_t pos = -1;
ObXmlNode *parent = NULL;
ObXmlElement *ele_node = NULL;
ObXmlAttribute *ns_node = NULL;
ObString key;
bool is_default_ns = false;
if (OB_ISNULL(old_node) || OB_ISNULL(update_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("node is NULL", K(ret), K(old_node), K(update_node));
} else if (FALSE_IT(ns_node = static_cast<ObXmlAttribute *>(old_node))) {
} else if (OB_ISNULL(parent = ns_node->get_parent())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("attribute parent node is NULL", K(ret));
} else if (FALSE_IT(key = ns_node->get_key())) {
} else if (FALSE_IT(is_default_ns = 0 == key.compare(ObXmlConstants::XMLNS_STRING))) {
} else if (parent->type() != M_ELEMENT) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("parent of namespace node is not an element node", K(ret), K(parent->type()));
} else if (FALSE_IT(ele_node = static_cast<ObXmlElement *>(parent))) {
} else if (OB_FAIL(ele_node->get_attribute_pos(ns_node->type(), key, pos))) {
LOG_WARN("fail to get attribute pos", K(ret), K(key), K(ns_node->type()));
} else if (!is_default_ns && OB_FAIL(update_exist_nodes_ns(ele_node, ns_node))) {
LOG_WARN("fail to update exist node ns", K(ret));
} else if (OB_FAIL(update_new_nodes_ns(allocator, ele_node, update_node))) {
LOG_WARN("fail to update new node ns", K(ret));
} else {
// remove prefix ns: not default ns && ns of element is not this prefix && attr of element not use, remove the prefix xmlns
if (!is_default_ns && ele_node->get_ns() != ns_node && !ele_node->has_attribute_with_ns(ns_node)) {
if (OB_FAIL(ele_node->remove_namespace(pos))) {
LOG_WARN("fail to remove prefix namespace", K(ret), K(key));
}
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(remove_and_insert_element_node(ele_node, update_node, 0, false))) {
LOG_WARN("fail to update element node", K(ret), K(pos));
}
}
return ret;
}
// update the descendent node prefix ns when need to remove parent node prefix ns
int ObExprUpdateXml::update_exist_nodes_ns(ObXmlElement *parent, ObXmlAttribute *prefix_ns)
{
int ret = OB_SUCCESS;
ObXmlAttribute *ns = NULL;
if (OB_ISNULL(parent) || OB_ISNULL(prefix_ns)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("node is NULL", K(ret), K(parent), K(prefix_ns));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < parent->size(); i++) {
if (OB_FAIL(set_ns_recrusively(parent->at(i), prefix_ns))) {
LOG_WARN("fail to set exist nodes ns", K(ret));
}
}
}
return ret;
}
// update the new node default ns to empty when the parent node has default ns
int ObExprUpdateXml::update_new_nodes_ns(ObIAllocator &allocator, ObXmlNode *parent, ObXmlNode *update_node)
{
int ret = OB_SUCCESS;
ObXmlAttribute *empty_ns = NULL;
ObXmlAttribute *default_ns = NULL;
if (OB_ISNULL(parent) || OB_ISNULL(update_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("node is NULL", K(ret), K(parent), K(update_node));
} else if (OB_FAIL(get_valid_default_ns_from_parent(parent, default_ns))) {
LOG_WARN("unexpected error in find default ns from parent", K(ret));
} else if (OB_NOT_NULL(default_ns) && !default_ns->get_value().empty()) {
// need to update the new node default ns with empty default ns
if (OB_ISNULL(empty_ns = OB_NEWx(ObXmlAttribute, (&allocator), ObMulModeNodeType::M_NAMESPACE, parent->get_mem_ctx()))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("alloc failed", K(ret));
} else {
empty_ns->set_key(ObXmlConstants::XMLNS_STRING);
empty_ns->set_value(ObString::make_empty_string());
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(set_ns_recrusively(update_node, empty_ns))) {
LOG_WARN("fail to set empty default ns recrusively", K(ret));
}
}
return ret;
}
// found valid default ns from down to top
int ObExprUpdateXml::get_valid_default_ns_from_parent(ObXmlNode *cur_node, ObXmlAttribute* &default_ns)
{
int ret = OB_SUCCESS;
ObXmlNode* t_node = NULL;
bool is_found = false;
if (OB_ISNULL(cur_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret));
} else if (!ObXMLExprHelper::is_xml_element_node(cur_node->type())) {
t_node = cur_node->get_parent();
} else {
t_node = cur_node;
}
while(!is_found && OB_SUCC(ret) && OB_NOT_NULL(t_node)) {
ObXmlElement *t_element = static_cast<ObXmlElement*>(t_node);
ObArray<ObIMulModeBase *> attr_list;
if (OB_FAIL(t_element->get_namespace_list(attr_list))) {
LOG_WARN("fail to get namespace list", K(ret));
}
for (int i = 0; !is_found && OB_SUCC(ret) && i < attr_list.size(); i ++) {
ObXmlAttribute *attr = static_cast<ObXmlAttribute *>(attr_list.at(i));
if (attr->get_key().compare(ObXmlConstants::XMLNS_STRING) == 0) {
is_found = true;
default_ns = attr;
}
}
t_node = t_node->get_parent();
}
return ret;
}
int ObExprUpdateXml::set_ns_recrusively(ObXmlNode *update_node, ObXmlAttribute *ns)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(update_node) || OB_ISNULL(ns)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update node is NULL", K(ret), K(update_node), K(ns));
} else if (!ObXMLExprHelper::is_xml_element_node(update_node->type())) {
// no need to set default ns
} else {
bool is_stop = false;
ObXmlElement *ele_node = static_cast<ObXmlElement *>(update_node);
ObString key = ns->get_key();
if (ele_node->type() != M_ELEMENT) {
// skip
} else if (key.compare(ObXmlConstants::XMLNS_STRING) == 0) {
// update default ns
if (ele_node->get_prefix().empty()) {
// this condition mean: has no ns || has non-empty default ns
is_stop = true;
if (OB_ISNULL(ele_node->get_ns())) {
ele_node->add_attribute(ns, false, 0);
ele_node->set_ns(ns);
} else { /* has non-empty default ns, skip and stop find */ }
}
} else { // has prefix
ObXmlAttribute *tmp_ns = NULL;
if (ele_node->get_ns() == ns ||
ele_node->has_attribute_with_ns(ns) ||
OB_NOT_NULL(tmp_ns = ele_node->get_ns_by_name(key))) {
// match condition below will stop recrusive
// element use this prefix ns || attributes of element use this prefix ns || this prefix in attributes
is_stop = true;
if (OB_NOT_NULL(tmp_ns)) { // if the prefix not in attributes
} else if (OB_FAIL(ele_node->add_attribute(ns, false, 0))) {
LOG_WARN("fail to add namespace node", K(ret), K(key));
}
}
}
if (!is_stop) {
// find its child node recrusivle when no need to set default ns
for (int64_t i = 0; OB_SUCC(ret) && i < ele_node->size(); i++) {
if (OB_FAIL(SMART_CALL(set_ns_recrusively(ele_node->at(i), ns)))) {
LOG_WARN("fail set default ns in origin tree recursively", K(ret));
}
} // end for
} // end is_stop
}
return ret;
}
// for xml nodes other than xmlattribute(including attribute and namespace)
int ObExprUpdateXml::update_xml_child_node(ObIAllocator &allocator, ObXmlNode *old_node, ObXmlNode *update_node)
{
int ret = OB_SUCCESS;
ObXmlNode *parent = NULL;
ObXmlElement *ele_node = NULL;
int64_t pos = -1;
if (OB_ISNULL(old_node) || OB_ISNULL(update_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("node is NULL", K(ret), K(old_node), K(update_node));
} else if (OB_ISNULL(parent = old_node->get_parent())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("parent node is NULL", K(ret));
} else {
ele_node = static_cast<ObXmlElement *>(parent);
pos = old_node->get_index();
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(update_new_nodes_ns(allocator, ele_node, update_node))) {
LOG_WARN("fail to update new node ns", K(ret));
} else if (OB_FAIL(remove_and_insert_element_node(ele_node, update_node, pos, true))) {
LOG_WARN("fail to update element node", K(ret));
}
return ret;
}
int ObExprUpdateXml::remove_and_insert_element_node(ObXmlElement *ele_node, ObXmlNode *update_node, int64_t pos, bool is_remove)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(ele_node) || OB_ISNULL(update_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("node is NULL", K(ret), K(ele_node), K(update_node));
} else if (pos < 0 || pos > ele_node->count()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("pos is invalid", K(ret), K(pos));
} else if (ObXMLExprHelper::is_xml_root_node(update_node->type())) {
if ((is_remove && ele_node->count() == 0) || update_node->count() == 0) {
// skip and do nothing
} else if (is_remove && OB_FAIL(ele_node->remove_element(ele_node->at(pos)))) { // remove the node
LOG_WARN("fail to remove element node", K(ret), K(pos));
} else {
ObXmlDocument *xml_doc = static_cast<ObXmlDocument *>(update_node);
for (int64_t i = 0; OB_SUCC(ret) && i < xml_doc->size(); i++) {
if (OB_ISNULL(xml_doc->at(i)) ) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("xml node is null", K(ret), K(i));
} else if (OB_FAIL(ele_node->add_element(xml_doc->at(i), false, pos + i))) { // insert the element node
LOG_WARN("fail to add element node", K(ret), K(i));
}
}
}
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected node type", K(ret), K(update_node->type()));
}
return ret;
}
#endif
int ObExprUpdateXml::cg_expr(ObExprCGCtx &expr_cg_ctx, const ObRawExpr &raw_expr, ObExpr &rt_expr) const
{
UNUSED(expr_cg_ctx);
UNUSED(raw_expr);
rt_expr.eval_func_ = eval_update_xml;
return OB_SUCCESS;
}
} // sql
} // oceanbase