870 lines
34 KiB
C++
870 lines
34 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 implement of func xml expr helper
|
|
*/
|
|
|
|
#define USING_LOG_PREFIX SQL_ENG
|
|
#include "lib/ob_errno.h"
|
|
#include "sql/engine/expr/ob_expr_xml_func_helper.h"
|
|
#include "sql/engine/expr/ob_expr_lob_utils.h"
|
|
#ifdef OB_BUILD_ORACLE_XML
|
|
#include "lib/xml/ob_xpath.h"
|
|
#include "lib/xml/ob_xml_bin.h"
|
|
#include "lib/xml/ob_xml_util.h"
|
|
#include "lib/xml/ob_xml_parser.h"
|
|
#endif // OB_BUILD_ORACLE_XML
|
|
#include "sql/engine/expr/ob_expr_sql_udt_utils.h"
|
|
#include "sql/session/ob_sql_session_info.h"
|
|
#include "sql/engine/ob_exec_context.h"
|
|
#include "sql/ob_result_set.h"
|
|
#include "sql/ob_spi.h"
|
|
#ifdef OB_BUILD_ORACLE_PL
|
|
#include "pl/sys_package/ob_sdo_geometry.h"
|
|
#endif
|
|
|
|
using namespace oceanbase::common;
|
|
using namespace oceanbase::sql;
|
|
|
|
namespace oceanbase
|
|
{
|
|
namespace sql
|
|
{
|
|
#ifdef OB_BUILD_ORACLE_XML
|
|
uint64_t ObXMLExprHelper::get_tenant_id(ObSQLSessionInfo *session)
|
|
{
|
|
uint64_t tenant_id = 0;
|
|
if (OB_ISNULL(session)) {
|
|
} else if (session->get_ddl_info().is_ddl_check_default_value()) {
|
|
tenant_id = OB_SERVER_TENANT_ID;
|
|
} else {
|
|
tenant_id = session->get_effective_tenant_id();
|
|
}
|
|
return tenant_id;
|
|
}
|
|
|
|
int ObXMLExprHelper::add_binary_to_element(ObMulModeMemCtx* mem_ctx, ObString binary_value, ObXmlElement &element)
|
|
{
|
|
INIT_SUCC(ret);
|
|
ObXmlDocument *xml_doc = NULL;
|
|
ObIMulModeBase *node = NULL;
|
|
ObXmlDocument *doc_node = NULL;
|
|
ObMulModeNodeType node_type = M_MAX_TYPE;
|
|
bool is_unparsed = false;
|
|
if (binary_value.empty()) {
|
|
if (OB_ISNULL(xml_doc = OB_NEWx(ObXmlDocument, (mem_ctx->allocator_),
|
|
ObMulModeNodeType::M_CONTENT,
|
|
(mem_ctx)))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("fail to create an empty xml content node", K(ret), K(binary_value));
|
|
} else {
|
|
node = xml_doc;
|
|
}
|
|
} else if (OB_FAIL(ObXmlUtil::xml_bin_type(binary_value, node_type))) {
|
|
LOG_WARN("xml bin type failed", K(ret));
|
|
} else if (node_type == M_UNPARSED) {
|
|
ObStringBuffer* buffer = nullptr;
|
|
ObXmlDocument *x_doc = nullptr;
|
|
ObString xml_text;
|
|
ObXmlBin bin(binary_value, mem_ctx);
|
|
if (OB_ISNULL(buffer = OB_NEWx(ObStringBuffer, mem_ctx->allocator_, (mem_ctx->allocator_)))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("fail to allocate buffer", K(ret));
|
|
} else if (OB_FAIL(bin.parse())) {
|
|
LOG_WARN("fail to parse binary.", K(ret));
|
|
} else if (OB_FAIL(bin.print_xml(*buffer, ObXmlFormatType::NO_FORMAT, 0, 0))) {
|
|
LOG_WARN("fail to print xml", K(ret));
|
|
} else if (FALSE_IT(xml_text.assign_ptr(buffer->ptr(), buffer->length()))) {
|
|
} else if (OB_FAIL(ObXmlParserUtils::parse_content_text(mem_ctx, xml_text, x_doc))) {
|
|
LOG_DEBUG("fail to parse unparse", K(ret));
|
|
ret = OB_SUCCESS;
|
|
if (OB_FAIL(bin.to_tree(node))) {
|
|
LOG_WARN("fail to tree", K(ret));
|
|
}
|
|
is_unparsed = true;
|
|
} else {
|
|
node = x_doc;
|
|
}
|
|
} else if (OB_FAIL(ObMulModeFactory::get_xml_base(mem_ctx, binary_value,
|
|
ObNodeMemType::BINARY_TYPE,
|
|
ObNodeMemType::TREE_TYPE,
|
|
node))) {
|
|
LOG_WARN("fail to get xml base", K(ret), K(binary_value), K(node_type));
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_ISNULL(node)) {
|
|
ret = OB_BAD_NULL_ERROR;
|
|
LOG_WARN("node cast to xmldocument failed", K(ret), K(node));
|
|
} else if (ObMulModeNodeType::M_DOCUMENT == node->type() ||
|
|
ObMulModeNodeType::M_CONTENT == node->type() ||
|
|
ObMulModeNodeType::M_UNPARSED == node->type()) {
|
|
if (OB_ISNULL(doc_node = static_cast<ObXmlDocument*>(node))) {
|
|
ret = OB_BAD_NULL_ERROR;
|
|
LOG_WARN("node cast to xmldocument failed", K(ret), K(node));
|
|
}
|
|
for (int32_t j = 0; OB_SUCC(ret) && j < doc_node->size(); j++) {
|
|
ObXmlNode *xml_node = doc_node->at(j);
|
|
if (OB_ISNULL(xml_node)) {
|
|
ret = OB_BAD_NULL_ERROR;
|
|
LOG_WARN("doc node get null", K(ret), K(j));
|
|
} else if (OB_XML_TYPE != xml_node->data_type()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("xml node date type unexpect", K(ret), K(j), K(xml_node->data_type()));
|
|
} else if (OB_FAIL(element.add_element(xml_node))) {
|
|
LOG_WARN("add element failed", K(ret), K(j), K(xml_node));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && is_unparsed) {
|
|
// if the binary is unparsed and parsed fail, set the element is unparsed
|
|
element.set_unparse(1);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::get_xml_base(ObMulModeMemCtx *ctx,
|
|
ObDatum *xml_datum,
|
|
ObCollationType cs_type,
|
|
ObNodeMemType expect_type,
|
|
ObIMulModeBase *&node)
|
|
{
|
|
ObMulModeNodeType node_type = M_MAX_TYPE;
|
|
return get_xml_base(ctx, xml_datum, cs_type, expect_type, node, node_type, false);
|
|
}
|
|
int ObXMLExprHelper::get_xml_base(ObMulModeMemCtx *ctx,
|
|
ObDatum *xml_datum,
|
|
ObCollationType cs_type,
|
|
ObNodeMemType expect_type,
|
|
ObIMulModeBase *&node,
|
|
ObMulModeNodeType &node_type,
|
|
bool is_reparse)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
// temporary use until xml binary ready
|
|
ObString xml_text;
|
|
ObDatumMeta xml_meta;
|
|
xml_meta.type_ = ObLongTextType;
|
|
xml_meta.cs_type_ = CS_TYPE_UTF8MB4_BIN;
|
|
ObXmlDocument *xml_doc = NULL;
|
|
if (OB_ISNULL(xml_datum)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("xml datum is NULL", K(ret));
|
|
} else if (OB_FAIL(ObTextStringHelper::read_real_string_data(*ctx->allocator_, *xml_datum, xml_meta, true, xml_text))) {
|
|
LOG_WARN("fail to get real data.", K(ret), K(xml_text));
|
|
} else if (xml_text.empty()) {
|
|
// create an empty xml node
|
|
if (OB_ISNULL(xml_doc = OB_NEWx(ObXmlDocument, (ctx->allocator_), ObMulModeNodeType::M_CONTENT, ctx))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("fail to create an empty xml content node", K(ret), K(xml_text));
|
|
} else {
|
|
node = xml_doc;
|
|
}
|
|
} else if (OB_FAIL(ObXmlUtil::xml_bin_type(xml_text, node_type))) {
|
|
LOG_WARN("failed to get bin header.", K(ret));
|
|
} else if (is_reparse) {
|
|
ObStringBuffer *buff = nullptr;
|
|
ParamPrint param_list;
|
|
if (OB_ISNULL(buff = OB_NEWx(ObStringBuffer, ctx->allocator_, (ctx->allocator_)))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("create obstrinbuffer failed", K(ret));
|
|
} else if (OB_FAIL(ObMulModeFactory::get_xml_base(ctx, xml_text,
|
|
ObNodeMemType::BINARY_TYPE,
|
|
ObNodeMemType::BINARY_TYPE,
|
|
node))) {
|
|
LOG_WARN("fail to get xml base", K(ret));
|
|
} else if (node_type == M_UNPARESED_DOC && OB_FALSE_IT(node_type = M_DOCUMENT)) {
|
|
} else if (node_type == M_UNPARSED) {
|
|
} else if (node_type == M_DOCUMENT && OB_FAIL(node->print_document(*buff, CS_TYPE_INVALID, ObXmlFormatType::NO_FORMAT, 0))) {
|
|
LOG_WARN("failed to convert xml binary to xml text", K(ret));
|
|
} else if (node_type == M_CONTENT && OB_FAIL(node->print_content(*buff, false, false, ObXmlFormatType::NO_FORMAT, param_list))) {
|
|
LOG_WARN("failed to convert xml binary to xml text", K(ret));
|
|
} else {
|
|
xml_text.assign_ptr(buff->ptr(), buff->length());
|
|
if (node_type != ObMulModeNodeType::M_DOCUMENT && OB_FAIL(ObXmlParserUtils::parse_content_text(ctx, xml_text, xml_doc))) {
|
|
LOG_WARN("fail to get xml content tree", K(ret), K(node_type));
|
|
} else if (node_type == ObMulModeNodeType::M_DOCUMENT && OB_FAIL(ObXmlParserUtils::parse_document_text(ctx, xml_text, xml_doc))) {
|
|
LOG_WARN("fail to get xml tree", K(ret), K(node_type));
|
|
} else {
|
|
xml_doc->set_xml_type(node_type);
|
|
node = xml_doc;
|
|
}
|
|
}
|
|
} else if (OB_FAIL(ObMulModeFactory::get_xml_base(ctx, xml_text,
|
|
ObNodeMemType::BINARY_TYPE,
|
|
expect_type,
|
|
node))) {
|
|
LOG_WARN("fail to get xml base", K(ret));
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
} else if (node->get_unparse() || node->type() == M_UNPARSED) { // unparse try to pasre
|
|
if (OB_FAIL(try_to_parse_unparse_binary(ctx, cs_type, node, expect_type, node))) {
|
|
LOG_WARN("fail to parse unparse binary", K(ret), K(xml_text));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::try_to_parse_unparse_binary(ObMulModeMemCtx* mem_ctx,
|
|
ObCollationType cs_type,
|
|
ObIMulModeBase *input_node,
|
|
ObNodeMemType expect_type,
|
|
ObIMulModeBase *&res_node)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
// serialzie unparese string
|
|
ObString unparse_str;
|
|
ObXmlDocument *xml_doc = nullptr;
|
|
ObStringBuffer *buff = nullptr;
|
|
|
|
if (OB_ISNULL(input_node) || !(input_node->type() == M_DOCUMENT || input_node->type() == M_UNPARSED)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unparse node is NULL or not M_DOCUMENT", K(input_node));
|
|
} else if (OB_ISNULL(buff = OB_NEWx(ObStringBuffer, mem_ctx->allocator_, (mem_ctx->allocator_)))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("create obstrinbuffer failed", K(input_node));
|
|
} else {
|
|
if (OB_FAIL(input_node->print_document(*buff, cs_type, ObXmlFormatType::NO_FORMAT, 0))) {
|
|
LOG_WARN("fail to serialize unparse string", K(ret));
|
|
} else {
|
|
unparse_str.assign_ptr(buff->ptr(), buff->length());
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(ObXmlParserUtils::parse_document_text(mem_ctx, unparse_str, xml_doc))) {
|
|
if (ret == OB_ERR_PARSER_SYNTAX) {
|
|
if (OB_FAIL(ObXmlParserUtils::parse_content_text(mem_ctx, unparse_str, xml_doc))) {
|
|
if (ret == OB_ERR_PARSER_SYNTAX) {
|
|
ret = OB_ERR_XML_PARSE;
|
|
LOG_USER_ERROR(OB_ERR_XML_PARSE);
|
|
}
|
|
LOG_WARN("fail to parse xml doc", K(unparse_str), K(ret));
|
|
}
|
|
}
|
|
LOG_WARN("fail to parse xml doc", K(unparse_str), K(ret));
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (expect_type == BINARY_TYPE) {
|
|
ObXmlBin* bin = nullptr;
|
|
if (OB_ISNULL(bin = OB_NEWx(ObXmlBin, mem_ctx->allocator_, (mem_ctx)))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("fail to alloc binary", K(ret));
|
|
} else if (OB_FAIL(bin->parse_tree(xml_doc))) {
|
|
LOG_WARN("fail to serialize tree.", K(ret));
|
|
} else {
|
|
res_node = bin;
|
|
}
|
|
} else {
|
|
res_node = xml_doc;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::pack_xml_res(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res, ObXmlDocument* doc,
|
|
ObMulModeMemCtx* mem_ctx, ObMulModeNodeType node_type,
|
|
ObString &plain_text)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObString blob_locator;
|
|
ObExprStrResAlloc expr_res_alloc(expr, ctx);
|
|
ObTextStringResult blob_res(ObLongTextType, true, &expr_res_alloc);
|
|
ObString binary_str;
|
|
ObIMulModeBase *xml_base;
|
|
if (OB_ISNULL(doc)) {
|
|
if (plain_text.length() == 0) {
|
|
res.set_string(NULL, 0);
|
|
} else if (OB_FAIL(ObMulModeFactory::get_xml_base(mem_ctx, plain_text,
|
|
ObNodeMemType::TREE_TYPE, ObNodeMemType::BINARY_TYPE, xml_base,
|
|
node_type))) {
|
|
LOG_WARN("get xml base failed", K(ret), K(node_type));
|
|
} else if (OB_FAIL(xml_base->get_raw_binary(binary_str, mem_ctx->allocator_))) {
|
|
LOG_WARN("get raw binary failed", K(ret));
|
|
}
|
|
} else if (FALSE_IT(doc->set_xml_type(node_type))) {
|
|
} else if (OB_FAIL(doc->get_raw_binary(binary_str, mem_ctx->allocator_))) {
|
|
LOG_WARN("get raw binary failed", K(ret));
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(pack_binary_res(expr, ctx, binary_str, blob_locator))) {
|
|
LOG_WARN("pack binary res failed", K(ret));
|
|
} else {
|
|
res.set_string(blob_locator.ptr(), blob_locator.length());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::pack_binary_res(const ObExpr &expr, ObEvalCtx &ctx, ObString binary_str, ObString &blob_locator)
|
|
{
|
|
INIT_SUCC(ret);
|
|
ObExprStrResAlloc expr_res_alloc(expr, ctx);
|
|
ObTextStringResult blob_res(ObLongTextType, true, &expr_res_alloc);
|
|
int64_t total_length = binary_str.length();
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(blob_res.init(total_length))) {
|
|
LOG_WARN("failed to init blob res", K(ret), K(blob_res), K(total_length));
|
|
} else if (OB_FAIL(blob_res.append(binary_str))) {
|
|
LOG_WARN("failed to append xml binary data", K(ret), K(blob_res));
|
|
} else {
|
|
blob_res.get_result_buffer(blob_locator);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::parse_namespace_str(ObString &ns_str, ObString &prefix, ObString &uri)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const char *str = ns_str.ptr();
|
|
int64_t str_len = ns_str.length();
|
|
int64_t idx = 0;
|
|
const char *prefix_start = NULL;
|
|
int64_t prefix_len = 0;
|
|
const char *uri_start = NULL;
|
|
int64_t uri_len = 0;
|
|
if (idx + 4 < str_len &&
|
|
str[idx] == 'x' &&
|
|
str[idx+1] == 'm' &&
|
|
str[idx+2] == 'l' &&
|
|
str[idx+3] == 'n' &&
|
|
str[idx+4] == 's') {
|
|
idx += 5;
|
|
if (str[idx] == ':') {
|
|
// parse prefix name
|
|
int64_t start = idx + 1;
|
|
while (idx < str_len && str[idx] != '=') ++idx;
|
|
if (idx < str_len && str[idx] == '=') {
|
|
prefix_start = str + start;
|
|
prefix_len = idx - start;
|
|
}
|
|
}
|
|
if (idx < str_len && str[idx] == '=') {
|
|
// parse uri value
|
|
idx += 1;
|
|
if (idx < str_len && str[idx] == '"') {
|
|
// "xxx"
|
|
int start = ++idx;
|
|
while(idx < str_len && str[idx] != '"') ++idx;
|
|
if (idx < str_len && str[idx] == '"') {
|
|
uri_start = str + start;
|
|
uri_len = idx - start;
|
|
idx += 1;
|
|
} else {
|
|
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
|
|
LOG_WARN("not invalid xml namespace string", K(ret), K(ns_str), K(idx));
|
|
}
|
|
} else {
|
|
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
|
|
LOG_WARN("not invalid xml namespace string", K(ret), K(ns_str), K(idx));
|
|
}
|
|
} else {
|
|
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
|
|
LOG_WARN("not invalid xml namespace string", K(ret), K(ns_str), K(idx));
|
|
}
|
|
} else {
|
|
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
|
|
LOG_WARN("not invalid xml namespace string", K(ret), K(ns_str), K(idx));
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (prefix_len > 0) {
|
|
prefix.assign_ptr(prefix_start, prefix_len);
|
|
}
|
|
uri.assign_ptr(uri_start, uri_len);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::construct_namespace_params(ObString &namespace_str,
|
|
ObString &default_ns,
|
|
ObPathVarObject &prefix_ns,
|
|
ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObString, 8> namespace_arr;
|
|
ObString trim_str = namespace_str.trim_space_only();
|
|
if (trim_str.empty()) {
|
|
/* nothing */
|
|
} else if (OB_FAIL(split_on(trim_str, ' ', namespace_arr))) {
|
|
LOG_WARN("fail to split on trim string", K(ret), K(trim_str));
|
|
} else {
|
|
for (int64_t i = 0; i < namespace_arr.count() && OB_SUCC(ret); i++) {
|
|
ObString prefix;
|
|
ObString uri;
|
|
if (OB_FAIL(parse_namespace_str(namespace_arr.at(i), prefix, uri))) {
|
|
LOG_WARN("fail to parse namespace str", K(ret), K(i), K(namespace_arr.at(i)));
|
|
} else if (prefix.empty()) {
|
|
default_ns = uri;
|
|
} else {
|
|
if (uri.empty()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("uri is not allow to empty", K(ret), K(prefix));
|
|
} else if (OB_FAIL(add_ns_to_container_node(prefix_ns, prefix, uri, allocator))) {
|
|
LOG_WARN("fail to add prefix namespace node", K(ret), K(prefix), K(uri));
|
|
}
|
|
}
|
|
} // end for
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::add_ns_to_container_node(ObPathVarObject &container,
|
|
ObString &prefix,
|
|
ObString &uri,
|
|
ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObDatum *datum;
|
|
if (OB_ISNULL(datum = OB_NEWx(ObDatum, (&allocator)))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("fail to alloc datum", K(ret));
|
|
} else if (FALSE_IT(datum->set_string(uri))) {
|
|
} else if (OB_FAIL(container.add(prefix, datum))) {
|
|
LOG_WARN("fail to add prefix namespace to ObPathVarObject", K(ret), K(*datum));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::set_string_result(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res, ObString &res_str)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObTextStringDatumResult text_result(expr.datum_meta_.type_, &expr, &ctx, &res);
|
|
int64_t res_len = res_str.length();
|
|
if (OB_FAIL(text_result.init(res_len))) {
|
|
LOG_WARN("fail to init string result length", K(ret), K(text_result), K(res_len));
|
|
} else if (OB_FAIL(text_result.append(res_str))) {
|
|
LOG_WARN("fail to append xml format string", K(ret), K(res_str), K(text_result));
|
|
} else {
|
|
text_result.set_result();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::get_str_from_expr(const ObExpr *expr,
|
|
ObEvalCtx &ctx,
|
|
ObString &res,
|
|
ObIAllocator &allocator)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObDatum *datum = NULL;
|
|
ObObjType val_type = expr->datum_meta_.type_;
|
|
uint16_t sub_schema_id = expr->obj_meta_.get_subschema_id();
|
|
if (OB_FAIL(expr->eval(ctx, datum))) {
|
|
LOG_WARN("eval xml arg failed", K(ret));
|
|
} else if (!ob_is_string_type(val_type)) {
|
|
ret = OB_ERR_INVALID_TYPE_FOR_OP;
|
|
LOG_WARN("input type error", K(val_type));
|
|
} else if (FALSE_IT(res = datum->get_string())) {
|
|
} else if (OB_FAIL(ObTextStringHelper::read_real_string_data(allocator, *datum,
|
|
expr->datum_meta_, expr->obj_meta_.has_lob_header(), res))) {
|
|
LOG_WARN("fail to get real data.", K(ret), K(res));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::parse_xml_str(ObMulModeMemCtx *ctx, const ObString &xml_text, ObXmlDocument *&xml_doc)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if(ObXmlParserUtils::has_xml_decl(xml_text)) {
|
|
if (OB_FAIL(ObXmlParserUtils::parse_document_text(ctx, xml_text, xml_doc))) {
|
|
if (ret == OB_ERR_PARSER_SYNTAX) {
|
|
ret = OB_ERR_XML_PARSE;
|
|
LOG_USER_ERROR(OB_ERR_XML_PARSE);
|
|
}
|
|
LOG_WARN("fail to parse xml doc", K(xml_text), K(ret));
|
|
}
|
|
} else if(OB_FAIL(ObXmlParserUtils::parse_content_text(ctx, xml_text, xml_doc))) {
|
|
if (ret == OB_ERR_PARSER_SYNTAX) {
|
|
ret = OB_ERR_XML_PARSE;
|
|
LOG_USER_ERROR(OB_ERR_XML_PARSE);
|
|
}
|
|
LOG_WARN("fail to parse xml doc", K(xml_text), K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::get_xmltype_from_expr(const ObExpr *expr,
|
|
ObEvalCtx &ctx,
|
|
ObDatum *&xml_datum)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObObjType val_type = expr->datum_meta_.type_;
|
|
uint16_t sub_schema_id = expr->obj_meta_.get_subschema_id();
|
|
if (!ob_is_xml_sql_type(val_type, sub_schema_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(val_type)));
|
|
} else if (OB_FAIL(expr->eval(ctx, xml_datum))) {
|
|
LOG_WARN("eval xml arg failed", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ObXMLExprHelper::is_xml_leaf_node(ObMulModeNodeType node_type)
|
|
{
|
|
return node_type == ObMulModeNodeType::M_ATTRIBUTE ||
|
|
node_type == ObMulModeNodeType::M_NAMESPACE ||
|
|
node_type == ObMulModeNodeType::M_CDATA ||
|
|
node_type == ObMulModeNodeType::M_TEXT;
|
|
}
|
|
|
|
bool ObXMLExprHelper::is_xml_text_node(ObMulModeNodeType node_type)
|
|
{
|
|
return node_type == ObMulModeNodeType::M_CDATA ||
|
|
node_type == ObMulModeNodeType::M_TEXT;
|
|
}
|
|
|
|
bool ObXMLExprHelper::is_xml_attribute_node(ObMulModeNodeType node_type)
|
|
{
|
|
return node_type == ObMulModeNodeType::M_ATTRIBUTE ||
|
|
node_type == ObMulModeNodeType::M_NAMESPACE;
|
|
}
|
|
|
|
bool ObXMLExprHelper::is_xml_element_node(ObMulModeNodeType node_type)
|
|
{
|
|
return node_type == ObMulModeNodeType::M_ELEMENT ||
|
|
node_type == ObMulModeNodeType::M_DOCUMENT ||
|
|
node_type == ObMulModeNodeType::M_CONTENT;
|
|
}
|
|
|
|
bool ObXMLExprHelper::is_xml_root_node(ObMulModeNodeType node_type)
|
|
{
|
|
return node_type == ObMulModeNodeType::M_DOCUMENT ||
|
|
node_type == ObMulModeNodeType::M_CONTENT;
|
|
}
|
|
|
|
void ObXMLExprHelper::replace_xpath_ret_code(int &ret)
|
|
{
|
|
if (ret == OB_OP_NOT_ALLOW) {
|
|
ret = OB_XPATH_EXPRESSION_UNSUPPORTED;
|
|
} else if (ret == OB_ERR_PARSER_SYNTAX) {
|
|
ret = OB_ERR_XML_PARSE;
|
|
} else if (ret == OB_ALLOCATE_MEMORY_FAILED) {
|
|
// do nothing
|
|
} else if (ret == OB_ERR_WRONG_VALUE) {
|
|
ret = OB_ERR_INVALID_INPUT;
|
|
} else {
|
|
ret = OB_ERR_INVALID_XPATH_EXPRESSION;
|
|
}
|
|
}
|
|
|
|
int ObXMLExprHelper::check_xml_document_unparsed(ObMulModeMemCtx* mem_ctx, ObString binary_str, bool &validity)
|
|
{
|
|
INIT_SUCC(ret);
|
|
ObMulModeNodeType node_type = M_MAX_TYPE;
|
|
if (OB_FAIL(ObXmlUtil::xml_bin_type(binary_str, node_type))) {
|
|
LOG_WARN("get xml bin type failed", K(ret));
|
|
} else {
|
|
ObXmlParser parser(mem_ctx);
|
|
parser.set_only_syntax_check();
|
|
if (node_type == M_UNPARESED_DOC && OB_FAIL(parser.parse_document(binary_str))) {
|
|
ret = OB_SUCCESS;
|
|
validity = false;
|
|
} else {
|
|
validity = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::parse_xml_document_unparsed(ObMulModeMemCtx* mem_ctx, ObString binary_str, ObString &res_str, ObXmlDocument* &res_doc)
|
|
{
|
|
INIT_SUCC(ret);
|
|
ObMulModeNodeType node_type = M_MAX_TYPE;
|
|
ObXmlDocument* doc = nullptr;
|
|
ObXmlBin bin(mem_ctx);
|
|
ObIMulModeBase* tree = nullptr;
|
|
ObXmlParser parser(mem_ctx);
|
|
ObStringBuffer buff(mem_ctx->allocator_);
|
|
if (OB_FAIL(ObXmlUtil::xml_bin_type(binary_str, node_type))) {
|
|
LOG_WARN("get xml bin type failed", K(ret));
|
|
} else if (node_type == ObMulModeNodeType::M_UNPARESED_DOC) {
|
|
if (OB_FAIL(ObXmlParserUtils::parse_document_text(mem_ctx, binary_str, doc))) {
|
|
LOG_WARN("parse document text failed", K(ret));
|
|
} else if (FALSE_IT(tree = doc)) {
|
|
} else if (OB_FAIL(bin.parse_tree(tree))) {
|
|
LOG_WARN("parse tree failed", K(ret));
|
|
} else if (OB_FAIL(bin.print_document(buff, CS_TYPE_UTF8MB4_GENERAL_CI, ObXmlFormatType::NO_FORMAT))) {
|
|
LOG_WARN("print document failed");
|
|
} else if (OB_FAIL(parser.parse_document(buff.string()))) {
|
|
ret = OB_ERR_XML_PARSE;
|
|
LOG_USER_ERROR(OB_ERR_XML_PARSE);
|
|
LOG_WARN("parse xml plain text as document failed.", K(ret));
|
|
} else {
|
|
res_str = buff.string();
|
|
res_doc = parser.document();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::content_unparsed_binary_check_doc(ObMulModeMemCtx* mem_ctx, ObString binary_str, ObString &res_str) {
|
|
INIT_SUCC(ret);
|
|
ObMulModeNodeType node_type = M_MAX_TYPE;
|
|
ObXmlDocument* doc = nullptr;
|
|
ObXmlBin bin(mem_ctx);
|
|
ObIMulModeBase* tree = nullptr;
|
|
ObXmlParser parser(mem_ctx);
|
|
ObStringBuffer buff(mem_ctx->allocator_);
|
|
ObXmlDocument* new_doc = nullptr;
|
|
ParamPrint param_list; // unused
|
|
if (OB_FAIL(ObXmlUtil::xml_bin_type(binary_str, node_type))) {
|
|
LOG_WARN("get xml bin type failed", K(ret));
|
|
} else if (node_type == ObMulModeNodeType::M_UNPARSED) {
|
|
if (OB_FAIL(ObXmlParserUtils::parse_content_text(mem_ctx, binary_str, doc))) {
|
|
LOG_WARN("parse document text failed", K(ret));
|
|
} else if (FALSE_IT(tree = doc)) {
|
|
} else if (OB_FAIL(bin.parse_tree(tree))) {
|
|
LOG_WARN("parse tree failed", K(ret));
|
|
} else if (OB_FAIL(bin.print_content(buff, false, false, ObXmlFormatType::NO_FORMAT, param_list))) {
|
|
LOG_WARN("print document failed");
|
|
} else if (OB_FAIL(parser.parse_document(buff.string()))) {
|
|
// try to parse content intto document, if it fails, leave the content unchanged
|
|
ret = OB_SUCCESS;
|
|
res_str = binary_str;
|
|
} else if (FALSE_IT(new_doc = parser.document())) {
|
|
} else if (OB_FAIL(new_doc->get_raw_binary(res_str, mem_ctx->allocator_))) {
|
|
LOG_WARN("get raw binary failed", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::check_element_validity(ObMulModeMemCtx* mem_ctx, ObXmlElement *in_ele, ObXmlElement *&out_ele, bool &validity) {
|
|
INIT_SUCC(ret);
|
|
ObXmlParser parser(mem_ctx);
|
|
ObStringBuffer buff(mem_ctx->allocator_);
|
|
if (OB_ISNULL(in_ele)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("in_ele is NULL", K(ret));
|
|
} else {
|
|
in_ele->set_unparse(1);
|
|
if (OB_FAIL(in_ele->print_element(buff, 0, ObXmlFormatType::NO_FORMAT))) {
|
|
LOG_WARN("print document failed");
|
|
} else if (OB_FAIL(parser.parse_document(buff.string()))) {
|
|
ret = OB_SUCCESS;
|
|
validity = false;
|
|
} else {
|
|
validity = true;
|
|
in_ele->set_unparse(0);
|
|
ObXmlDocument* doc = parser.document();
|
|
if (OB_NOT_NULL(doc) && doc->size() > 0){
|
|
out_ele = static_cast<ObXmlElement *>(doc->at(0));
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("fail to get valid xml element", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::check_doc_validity(ObMulModeMemCtx* mem_ctx, ObXmlDocument *&doc, bool &validity) {
|
|
INIT_SUCC(ret);
|
|
ObXmlParser parser(mem_ctx);
|
|
ObStringBuffer buff(mem_ctx->allocator_);
|
|
if (OB_FAIL(doc->print_document(buff, CS_TYPE_UTF8MB4_GENERAL_CI, ObXmlFormatType::NO_FORMAT))) {
|
|
LOG_WARN("print document failed");
|
|
} else if (OB_FAIL(parser.parse_document(buff.string()))) {
|
|
ret = OB_SUCCESS;
|
|
validity = false;
|
|
} else if (OB_ISNULL(doc = parser.document())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("parser document return NULL", K(ret));
|
|
} else {
|
|
validity = true;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif // OB_BUILD_ORACLE_XML
|
|
// not all udts sql types based on lobs, so not handle xml in process_lob_locator_results
|
|
int ObXMLExprHelper::process_sql_udt_results(ObObj& value, sql::ObResultSet &result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObArenaAllocator *allocator = NULL;
|
|
sql::ObSQLSessionInfo *session_info = &result.get_session();
|
|
if (OB_FAIL(result.get_exec_context().get_convert_charset_allocator(allocator))) {
|
|
LOG_WARN("fail to get convert charset allocator", K(ret));
|
|
} else if (OB_ISNULL(allocator)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("lob fake allocator is null.", K(ret), K(value));
|
|
} else if (OB_FAIL(process_sql_udt_results(value,
|
|
allocator,
|
|
&result.get_session(),
|
|
&result.get_exec_context(),
|
|
result.is_ps_protocol()))) {
|
|
LOG_WARN("convert udt to client format failed.", K(ret), K(value));
|
|
} else { /* do nothing */ }
|
|
return ret;
|
|
}
|
|
|
|
int ObXMLExprHelper::process_sql_udt_results(common::ObObj& value,
|
|
common::ObIAllocator *allocator,
|
|
sql::ObSQLSessionInfo *session_info,
|
|
sql::ObExecContext *exec_context,
|
|
bool is_ps_protocol,
|
|
const ColumnsFieldIArray *fields,
|
|
ObSchemaGetterGuard *schema_guard) // need fields and schema guard
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (!value.is_user_defined_sql_type() && !value.is_collection_sql_type() && !value.is_geometry()) {
|
|
ret = OB_NOT_SUPPORTED;
|
|
OB_LOG(WARN, "not supported udt type", K(ret),
|
|
K(value.get_type()), K(value.get_udt_subschema_id()));
|
|
#ifdef OB_BUILD_ORACLE_XML
|
|
} else if (value.is_xml_sql_type()) {
|
|
bool is_client_support_binary_xml = false; // client receive xml as json, like json
|
|
if (value.is_null() || value.is_nop_value()) {
|
|
// do nothing
|
|
} else if (is_client_support_binary_xml) {
|
|
// convert to udt client format
|
|
} else {
|
|
ObString data;
|
|
ObLobLocatorV2 loc(value.get_string(), true);
|
|
ObString converted_str;
|
|
if (!loc.is_null()) {
|
|
ObTextStringIter instr_iter(ObLongTextType, CS_TYPE_BINARY, value.get_string(), true);
|
|
if (OB_FAIL(instr_iter.init(0, session_info, allocator))) {
|
|
LOG_WARN("init lob str inter failed", K(ret), K(value));
|
|
} else if (OB_FAIL(instr_iter.get_full_data(data))) {
|
|
LOG_WARN("get xml full data failed", K(value));
|
|
} else {
|
|
ObMulModeNodeType node_type = M_MAX_TYPE;
|
|
ObStringBuffer* jbuf = nullptr;
|
|
ParamPrint param_list;
|
|
param_list.indent = 2;
|
|
ObIMulModeBase *node = nullptr;
|
|
ObMulModeMemCtx* ctx = nullptr;
|
|
|
|
if (OB_FAIL(ObXmlUtil::create_mulmode_tree_context(allocator, ctx))) {
|
|
LOG_WARN("fail to create tree memory context", K(ret));
|
|
} else if (OB_ISNULL(allocator)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid input args", K(ret), KP(allocator));
|
|
} else if (data.length() == 0) {
|
|
} else if (OB_FAIL(ObXmlUtil::xml_bin_type(data, node_type))) {
|
|
LOG_WARN("xml bin type failed", K(ret));
|
|
} else if (OB_FAIL(ObMulModeFactory::get_xml_base(ctx, data,
|
|
ObNodeMemType::BINARY_TYPE,
|
|
ObNodeMemType::BINARY_TYPE,
|
|
node))) {
|
|
LOG_WARN("fail to get xml base", K(ret), K(data.length()));
|
|
}
|
|
|
|
ObCollationType cs_type = session_info->get_local_collation_connection();
|
|
|
|
if (OB_FAIL(ret) || data.length() == 0) {
|
|
} else if (OB_ISNULL(jbuf = OB_NEWx(ObStringBuffer, allocator, (allocator)))) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("fail to construct buffer", K(ret));
|
|
} else if ((ObMulModeNodeType::M_DOCUMENT == node_type ||
|
|
ObMulModeNodeType::M_UNPARESED_DOC == node_type) // /Although it is judged here that node_type=M_UNPARESED_DOC, it can actually go here, and the label has been replaced with DOCUMENT
|
|
&& OB_FAIL(node->print_document(*jbuf, cs_type, ObXmlFormatType::WITH_FORMAT))) {
|
|
LOG_WARN("print document failed", K(ret));
|
|
} else if ((ObMulModeNodeType::M_CONTENT == node_type ||
|
|
ObMulModeNodeType::M_UNPARSED == node_type) &&
|
|
OB_FAIL(node->print_content(*jbuf, false, false, ObXmlFormatType::WITH_FORMAT, param_list))) {
|
|
LOG_WARN("print content failed", K(ret));
|
|
} else {
|
|
data.assign_ptr(jbuf->ptr(), jbuf->length());
|
|
}
|
|
|
|
if (OB_SUCC(ret) && cs_type != CS_TYPE_UTF8MB4_BIN) {
|
|
if (OB_FAIL(ObCharset::charset_convert(*allocator, data, CS_TYPE_UTF8MB4_BIN,
|
|
cs_type, converted_str))) {
|
|
LOG_WARN("charset convertion failed", K(ret), K(data));
|
|
}
|
|
} else {
|
|
converted_str = data;
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
value.set_udt_value(converted_str.ptr(), converted_str.length());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // OB_BUILD_ORACLE_XML
|
|
} else if (value.is_geometry()) {
|
|
if (is_ps_protocol) {
|
|
#ifdef OB_BUILD_ORACLE_PL
|
|
ObObj result;
|
|
if (OB_ISNULL(exec_context)) {
|
|
ret = OB_BAD_NULL_ERROR;
|
|
} else if (OB_FAIL(pl::ObSdoGeometry::wkb_to_pl_extend(exec_context->get_allocator(), exec_context, value.get_string(), result))) {
|
|
LOG_WARN("failed to get geometry wkb from pl extend", K(ret));
|
|
} else {
|
|
value = result;
|
|
}
|
|
#else
|
|
ret = OB_NOT_SUPPORTED;
|
|
#endif
|
|
}
|
|
} else {
|
|
if (OB_ISNULL(exec_context)) {
|
|
ret = OB_BAD_NULL_ERROR;
|
|
} else if (OB_ISNULL(exec_context->get_physical_plan_ctx())) {
|
|
// no physical plan, build new one
|
|
if (OB_ISNULL(fields)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("no fields to rebuild udt meta", K(ret), K(lbt()));
|
|
} else if (OB_FAIL(exec_context->create_physical_plan_ctx())) {
|
|
LOG_WARN("failed to create physical plan ctx of subschema id", K(ret), K(lbt()));
|
|
} else if (OB_FAIL(exec_context->get_physical_plan_ctx()->build_subschema_by_fields(fields, schema_guard))) {
|
|
LOG_WARN("failed to rebuild_subschema by fields", K(ret), K(*fields), K(lbt()));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else {
|
|
const uint16_t subschema_id = value.get_meta().get_subschema_id();
|
|
ObSqlUDTMeta udt_meta;
|
|
if (OB_ISNULL(exec_context->get_physical_plan_ctx())) {
|
|
// build temp subschema id
|
|
} else if (OB_FAIL(exec_context->get_sqludt_meta_by_subschema_id(subschema_id, udt_meta))) {
|
|
LOG_WARN("failed to get udt meta", K(ret), K(subschema_id));
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (!ObObjUDTUtil::ob_is_supported_sql_udt(udt_meta.udt_id_)) {
|
|
ret = OB_NOT_SUPPORTED;
|
|
LOG_WARN("not supported to get udt meta", K(ret), K(udt_meta.udt_id_));
|
|
} else if (!is_ps_protocol) {
|
|
ObSqlUDT sql_udt;
|
|
sql_udt.set_udt_meta(udt_meta);
|
|
ObString res_str;
|
|
if (OB_FAIL(sql::ObSqlUdtUtils::convert_sql_udt_to_string(value, allocator, exec_context,
|
|
sql_udt, res_str))) {
|
|
LOG_WARN("failed to convert udt to string", K(ret), K(subschema_id));
|
|
} else {
|
|
value.set_udt_value(res_str.ptr(), res_str.length());
|
|
}
|
|
} else {
|
|
ObString udt_data = value.get_string();
|
|
ObObj result;
|
|
if (OB_FAIL(ObSqlUdtUtils::cast_sql_record_to_pl_record(exec_context,
|
|
result,
|
|
udt_data,
|
|
udt_meta))) {
|
|
LOG_WARN("failed to cast sql collection to pl collection", K(ret), K(udt_meta.udt_id_));
|
|
} else {
|
|
value = result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
} // sql
|
|
} // oceanbase
|