Server IP : 172.67.216.182 / Your IP : 172.71.82.68 Web Server : Apache System : Linux krdc-ubuntu-s-2vcpu-4gb-amd-blr1-01.localdomain 5.15.0-142-generic #152-Ubuntu SMP Mon May 19 10:54:31 UTC 2025 x86_64 User : www ( 1000) PHP Version : 7.4.33 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /www/server/mysql/src/sql/ |
Upload File : |
/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "json_dom.h" #include "json_path.h" #include "sql_class.h" // THD #include "sql_time.h" #include "sql_const.h" #include "derror.h" #include "base64.h" #include "m_string.h" // my_gcvt, _dig_vec_lower #include "mysqld_error.h" // ER_* #include "prealloced_array.h" // Prealloced_array #include "template_utils.h" // down_cast, pointer_cast #include "rapidjson/rapidjson.h" #include "rapidjson/reader.h" #include "rapidjson/memorystream.h" #include "rapidjson/error/en.h" #include <algorithm> // std::min, std::max #include <memory> // std::auto_ptr using namespace rapidjson; const char * Json_dom::json_type_string_map[]= { "NULL", "DECIMAL", "INTEGER", "UNSIGNED INTEGER", "DOUBLE", "STRING", "OBJECT", "ARRAY", "BOOLEAN", "DATE", "TIME", "DATETIME", "TIMESTAMP", "OPAQUE", "ERROR", // OPAQUE types with special names "BLOB", "BIT", "GEOMETRY", NULL }; /** Auto-wrap a dom. Delete the dom if there is a memory allocation failure. */ Json_array *wrap_in_array(Json_dom *dom) { Json_array *array= new (std::nothrow) Json_array(dom); if (array == NULL) delete dom; /* purecov: inspected */ return array; } /** A dom is mergeable if it is an array or an object. All other types must be wrapped in an array in order to be merged. Delete the candidate if there is a memory allocation failure. */ Json_dom *make_mergeable(Json_dom *candidate) { switch (candidate->json_type()) { case Json_dom::J_ARRAY: case Json_dom::J_OBJECT: { return candidate; } default: { return wrap_in_array(candidate); } } } Json_dom *merge_doms(Json_dom *left, Json_dom *right) { left= make_mergeable(left); if (!left) { /* purecov: begin inspected */ delete right; return NULL; /* purecov: end */ } right= make_mergeable(right); if (!right) { /* purecov: begin inspected */ delete left; return NULL; /* purecov: end */ } // at this point, the arguments are either objects or arrays bool left_is_array= (left->json_type() == Json_dom::J_ARRAY); bool right_is_array= (right->json_type() == Json_dom::J_ARRAY); if (left_is_array || right_is_array) { if (!left_is_array) left= wrap_in_array(left); if (!left) { /* purecov: begin inspected */ delete right; return NULL; /* purecov: end */ } if (!right_is_array) right= wrap_in_array(right); if (!right) { /* purecov: begin inspected */ delete left; return NULL; /* purecov: end */ } if (down_cast<Json_array *>(left)->consume(down_cast<Json_array *>(right))) { delete left; /* purecov: inspected */ return NULL; /* purecov: inspected */ } } else // otherwise, both doms are objects { if (down_cast<Json_object *>(left) ->consume(down_cast<Json_object *>(right))) { delete left; /* purecov: inspected */ return NULL; /* purecov: inspected */ } } return left; } void *Json_dom::operator new(size_t size, const std::nothrow_t&) throw() { /* Call my_malloc() with the MY_WME flag to make sure that it will write an error message if the memory could not be allocated. */ return my_malloc(key_memory_JSON, size, MYF(MY_WME)); } void Json_dom::operator delete(void *ptr) throw() { my_free(ptr); } /* This operator is included in order to silence warnings on some compilers. It is called if the constructor throws an exception when an object is allocated with nothrow new. This is not supposed to happen and is therefore hard to test, so annotate it to avoid cluttering the test coverage reports. */ /* purecov: begin inspected */ void Json_dom::operator delete(void *ptr, const std::nothrow_t&) throw() { operator delete(ptr); } /* purecov: end */ /** Compute the maximum length of the string representation of the Json type literals which we use as output from JSON_TYPE. @return the length of the longest literal + 1 (for terminating NUL). */ static uint32 compute_max_typelit() { size_t maxl= 0; for (const char **s= &Json_dom::json_type_string_map[0]; *s; ++s) { maxl= std::max(std::strlen(*s), maxl); } return static_cast<uint32>(maxl + 1); } const uint32 Json_dom::typelit_max_length= compute_max_typelit(); static bool seen_already(Json_dom_vector *result, Json_dom *cand) { Json_dom_vector::iterator it= std::find(result->begin(), result->end(), cand); return it != result->end(); } /** Add a value to a vector if it isn't already there. @param[in] candidate value to add @param[in,out] duplicates set of values added @param[in,out] result vector @return false on success, true on error */ static bool add_if_missing(Json_dom *candidate, Json_dom_vector *duplicates, Json_dom_vector *result) { if (duplicates->insert_unique(candidate).second) { return result->push_back(candidate); } return false; } /** Check if a seek operation performed by Json_dom::find_child_doms() or Json_dom::seek() is done. @return true if only one result is needed and a result has been found */ template <class Result_vector> static inline bool is_seek_done(const Result_vector *hits, bool only_need_one) { return only_need_one && hits->size() > 0; } bool Json_dom::find_child_doms(const Json_path_leg *path_leg, bool auto_wrap, bool only_need_one, Json_dom_vector *duplicates, Json_dom_vector *result) { Json_dom::enum_json_type dom_type= json_type(); enum_json_path_leg_type leg_type= path_leg->get_type(); if (is_seek_done(result, only_need_one)) return false; switch (leg_type) { case jpl_array_cell: { size_t array_cell_index= path_leg->get_array_cell_index(); if (dom_type == Json_dom::J_ARRAY) { const Json_array * const array= down_cast<const Json_array *>(this); if (array_cell_index < array->size() && add_if_missing((*array)[array_cell_index], duplicates, result)) return true; /* purecov: inspected */ } else if (array_cell_index == 0 && auto_wrap) { if (!seen_already(result, this)) { // auto-wrap non-arrays if (add_if_missing(this, duplicates, result)) return true; /* purecov: inspected */ } } return false; } case jpl_ellipsis: { if (add_if_missing(this, duplicates, result)) return true; /* purecov: inspected */ if (dom_type == Json_dom::J_ARRAY) { const Json_array * const array= down_cast<const Json_array *>(this); for (unsigned eidx= 0; eidx < array->size(); eidx++) { Json_dom * child= (*array)[eidx]; if (add_if_missing(child, duplicates, result)) return true; /* purecov: inspected */ if (is_seek_done(result, only_need_one)) return false; /* purecov: inspected */ Json_dom::enum_json_type child_type= child->json_type(); if ((child_type == Json_dom::J_ARRAY) || (child_type == Json_dom::J_OBJECT)) { // now recurse and add all objects and arrays under the child if (child->find_child_doms(path_leg, auto_wrap, only_need_one, duplicates, result)) return true; /* purecov: inspected */ } } // end of loop through children } else if (dom_type == Json_dom::J_OBJECT) { const Json_object *const object= down_cast<const Json_object *>(this); for (Json_object::const_iterator iter= object->begin(); iter != object->end(); ++iter) { Json_dom *child= iter->second; Json_dom::enum_json_type child_type= child->json_type(); if (add_if_missing(child, duplicates, result)) return true; /* purecov: inspected */ if (is_seek_done(result, only_need_one)) return false; /* purecov: inspected */ if ((child_type == Json_dom::J_ARRAY) || (child_type == Json_dom::J_OBJECT)) { // now recurse and add all objects and arrays under the child if (child->find_child_doms(path_leg, auto_wrap, only_need_one, duplicates, result)) return true; /* purecov: inspected */ } } // end of loop through children } return false; } case jpl_array_cell_wildcard: { if (dom_type == Json_dom::J_ARRAY) { const Json_array * array= down_cast<const Json_array *>(this); for (unsigned idx= 0; idx < array->size(); idx++) { if (add_if_missing((*array)[idx], duplicates, result)) return true; /* purecov: inspected */ if (is_seek_done(result, only_need_one)) return false; } } return false; } case jpl_member: { if (dom_type == Json_dom::J_OBJECT) { const Json_object * object= down_cast<const Json_object *>(this); std::string member_name(path_leg->get_member_name(), path_leg->get_member_name_length()); Json_dom * child= object->get(member_name); if (child != NULL && add_if_missing(child, duplicates, result)) return true; /* purecov: inspected */ } return false; } case jpl_member_wildcard: { if (dom_type == Json_dom::J_OBJECT) { const Json_object * object= down_cast<const Json_object *>(this); for (Json_object::const_iterator iter= object->begin(); iter != object->end(); ++iter) { if (add_if_missing(iter->second, duplicates, result)) return true; /* purecov: inspected */ if (is_seek_done(result, only_need_one)) return false; } } return false; } } /* purecov: begin deadcode */ DBUG_ABORT(); return true; /* purecov: end */ } Json_object::Json_object() : Json_dom(), m_map(Json_object_map::key_compare(), Json_object_map::allocator_type(key_memory_JSON)) {} Json_object::~Json_object() { clear(); } /** Check if the depth of a JSON document exceeds the maximum supported depth (JSON_DOCUMENT_MAX_DEPTH). Raise an error if the maximum depth has been exceeded. @param[in] depth the current depth of the document @return true if the maximum depth is exceeded, false otherwise */ static bool check_json_depth(size_t depth) { if (depth > JSON_DOCUMENT_MAX_DEPTH) { my_error(ER_JSON_DOCUMENT_TOO_DEEP, MYF(0)); return true; } return false; } /** This class overrides the methods on BaseReaderHandler to make out own handler which will construct our DOM from the parsing of the JSON text. <code> bool Null() { } bool Bool(bool) { } bool Int(int) { } bool Uint(unsigned) { } bool Int64(int64_t) { } bool Uint64(uint64_t) { } bool Double(double) { } bool String(const Ch*, SizeType, bool) { } bool StartObject() { } bool Key() { } bool EndObject(SizeType) { } bool StartArray() { } bool EndArray(SizeType) { } </code> @see Json_dom::parse */ class Rapid_json_handler : public BaseReaderHandler<UTF8<> > { private: // std::cerr << "callback " << name << ':' << state << '\n'; std::cerr.flush() #define DUMP_CALLBACK(name,state) enum enum_state { expect_anything, expect_array_value, expect_object_key, expect_object_value, expect_eof }; struct Current_element { Current_element(bool object, const char *str, uint32 length, Json_dom *value) : m_object(object), m_key(std::string(str, length)), m_value(value) {} Current_element(Json_dom *value) : m_object(false), m_value(value) {} bool m_object; //!< true of object, false if array std::string m_key; //!< only used if object Json_dom *m_value; //!< deallocated by clients }; typedef Prealloced_array<Current_element, 8, false> Element_vector; struct Partial_compound { Partial_compound(bool is_object) : m_elements(key_memory_JSON), m_is_object(is_object) {} ~Partial_compound() {} Element_vector m_elements; bool m_is_object; }; typedef Prealloced_array<Partial_compound, 8, false> Compound_vector; enum_state m_state; Compound_vector m_stack; Json_dom *m_dom_as_built; bool m_preserve_neg_zero_int; public: Rapid_json_handler(bool preserve_neg_zero_int= false) : m_state(expect_anything), m_stack(key_memory_JSON), m_dom_as_built(NULL), m_preserve_neg_zero_int(preserve_neg_zero_int) {} ~Rapid_json_handler() { if (m_dom_as_built) { // We managed to build something, but found garbage after it delete m_dom_as_built; } else { // We have something half built, empty the allocated data in it for (Compound_vector::iterator iter= m_stack.begin(); iter != m_stack.end(); ++iter) { for (Element_vector::iterator i= iter->m_elements.begin(); i != iter->m_elements.end(); ++i) { delete i->m_value; } } } } /** @returns The built JSON DOM object. Deallocation is the returned value responsibility of the caller. */ Json_dom *get_built_doc() { Json_dom *result= m_dom_as_built; m_dom_as_built= NULL; return result; } /** Function which is called on each scalar value found in the JSON document being parsed. @param[in] scalar the scalar that was seen @return true if parsing should continue, false if an error was found and parsing should stop */ bool seeing_scalar(Json_scalar *scalar) { std::auto_ptr<Json_scalar> aptr(scalar); if (scalar == NULL || check_json_depth(m_stack.size() + 1)) return false; switch (m_state) { case expect_anything: m_dom_as_built= scalar; m_state= expect_eof; break; case expect_array_value: if (m_stack.back().m_elements.push_back(Current_element(scalar))) return false; /* purecov: inspected */ break; case expect_object_key: case expect_eof: /* purecov: begin inspected */ DBUG_ABORT(); return false; /* purecov: end */ case expect_object_value: assert(!m_stack.back().m_elements.empty()); assert(m_stack.back().m_elements.back().m_value == NULL); m_stack.back().m_elements.back().m_value= scalar; m_state= expect_object_key; break; } /* The scalar is owned by the Element_vector or m_dom_as_built now, so release it. */ aptr.release(); return true; } bool Null() { DUMP_CALLBACK("null", state); return seeing_scalar(new (std::nothrow) Json_null()); } bool Bool(bool b) { DUMP_CALLBACK("bool", state); return seeing_scalar(new (std::nothrow) Json_boolean(b)); } bool Int(int i) { DUMP_CALLBACK("int", state); return seeing_scalar(new (std::nothrow) Json_int(i)); } bool Uint(unsigned u) { DUMP_CALLBACK("uint", state); return seeing_scalar(new (std::nothrow) Json_int(static_cast<longlong>(u))); } bool Int64(int64_t i) { DUMP_CALLBACK("int64", state); return seeing_scalar(new (std::nothrow) Json_int(i)); } bool Uint64(uint64_t ui64) { DUMP_CALLBACK("uint64", state); return seeing_scalar(new (std::nothrow) Json_uint(ui64)); } bool Double(double d, bool is_int= false) { if (is_int && !m_preserve_neg_zero_int) { /* The is_int flag is true only if -0 was seen. Handle it as an integer. */ assert(d == 0.0); return Int64(static_cast<int64_t>(d)); } else { DUMP_CALLBACK("double", state); return seeing_scalar(new (std::nothrow) Json_double(d)); } } bool String(const char* str, SizeType length, bool copy) { if (check_json_depth(m_stack.size() + 1)) return false; DUMP_CALLBACK("string", state); switch (m_state) { case expect_anything: m_dom_as_built= new (std::nothrow) Json_string(std::string(str, length)); if (!m_dom_as_built) return false; /* purecov: inspected */ m_state= expect_eof; break; case expect_array_value: { Json_string *jstr= new (std::nothrow) Json_string(std::string(str, length)); if (jstr == NULL || m_stack.back().m_elements.push_back(Current_element(jstr))) { /* purecov: begin inspected */ delete jstr; return false; /* purecov: end */ } break; } case expect_object_key: if (m_stack.back().m_elements.push_back(Current_element(true, str, length, NULL))) return false; /* purecov: inspected */ m_state= expect_object_value; break; case expect_eof: /* purecov: begin inspected */ DBUG_ABORT(); return false; /* purecov: end */ case expect_object_value: assert(!m_stack.back().m_elements.empty()); assert(m_stack.back().m_elements.back().m_value == NULL); m_stack.back().m_elements.back().m_value= new (std::nothrow) Json_string(std::string(str, length)); m_state= expect_object_key; break; } return true; } bool StartObject() { DUMP_CALLBACK("start object {", state); switch (m_state) { case expect_anything: case expect_array_value: case expect_object_value: if (m_stack.push_back(Partial_compound(true)) || check_json_depth(m_stack.size())) return false; m_state= expect_object_key; break; case expect_eof: case expect_object_key: /* purecov: begin inspected */ DBUG_ABORT(); return false; /* purecov: end */ } return true; } bool EndObject(SizeType) { DUMP_CALLBACK("} end object", state); switch (m_state) { case expect_object_key: { std::auto_ptr<Json_object> o(new (std::nothrow) Json_object()); if (o.get() == NULL) return false; /* purecov: inspected */ for (Element_vector::const_iterator iter= m_stack.back().m_elements.begin(); iter != m_stack.back().m_elements.end(); ++iter) { /* _alias: save superfluous copy/delete */ if (o->add_alias(iter->m_key, iter->m_value)) return false; /* purecov: inspected */ } m_stack.pop_back(); if (m_stack.empty()) { m_dom_as_built= o.release(); m_state= expect_eof; } else if (m_stack.back().m_is_object) { m_stack.back().m_elements.back().m_value= o.release(); m_state= expect_object_key; } else { if (m_stack.back().m_elements.push_back(o.get())) return false; /* purecov: inspected */ o.release(); // Owned by the Element_vector now. m_state= expect_array_value; } } break; case expect_array_value: case expect_eof: case expect_object_value: case expect_anything: /* purecov: begin inspected */ DBUG_ABORT(); return false; /* purecov: end */ } return true; } bool StartArray() { DUMP_CALLBACK("start array [", state); switch (m_state) { case expect_anything: case expect_array_value: case expect_object_value: if (m_stack.push_back(Partial_compound(false)) || check_json_depth(m_stack.size())) return false; m_state= expect_array_value; break; case expect_eof: case expect_object_key: /* purecov: begin inspected */ DBUG_ABORT(); return false; /* purecov: end */ } return true; } bool EndArray(SizeType) { DUMP_CALLBACK("] end array", state); switch (m_state) { case expect_array_value: { std::auto_ptr<Json_array> a(new (std::nothrow) Json_array()); if (a.get() == NULL) return false; /* purecov: inspected */ for (Element_vector::const_iterator iter= m_stack.back().m_elements.begin(); iter != m_stack.back().m_elements.end(); ++iter) { /* _alias: save superfluous copy/delete */ if (a->append_alias(iter->m_value)) return false; /* purecov: inspected */ } m_stack.pop_back(); if (m_stack.empty()) { m_dom_as_built= a.release(); m_state= expect_eof; } else { if (m_stack.back().m_is_object) { m_stack.back().m_elements.back().m_value= a.release(); m_state= expect_object_key; } else { if (m_stack.back().m_elements.push_back(a.get())) return false; /* purecov: inspected */ a.release(); // Owned by the Element_vector now. m_state= expect_array_value; } } } break; case expect_object_key: case expect_object_value: case expect_eof: case expect_anything: /* purecov: begin inspected */ DBUG_ABORT(); return false; /* purecov: end */ } return true; } bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } }; Json_dom *Json_dom::parse(const char *text, size_t length, const char **syntaxerr, size_t *offset, bool preserve_neg_zero_int) { Rapid_json_handler handler(preserve_neg_zero_int); MemoryStream ss(text, length); Reader reader; bool success= reader.Parse<kParseDefaultFlags>(ss, handler); if (success) { Json_dom *dom= handler.get_built_doc(); if (dom == NULL && syntaxerr != NULL) { // The parsing failed for some other reason than a syntax error. *syntaxerr= NULL; } return dom; } // Report the error offset and the error message if requested by the caller. if (offset != NULL) *offset= reader.GetErrorOffset(); if (syntaxerr != NULL) *syntaxerr= GetParseError_En(reader.GetParseErrorCode()); return NULL; } /** This class implements a handler for use with rapidjson::Reader when we want to check if a string is a valid JSON text. The handler does not build a DOM structure, so it is quicker than Json_dom::parse() in the cases where we don't care about the DOM, such as in the JSON_VALID() function. The handler keeps track of how deeply nested the document is, and it raises an error and stops parsing when the depth exceeds JSON_DOCUMENT_MAX_DEPTH. */ class Syntax_check_handler { private: size_t m_depth; ///< The current depth of the document bool seeing_scalar() { return !check_json_depth(m_depth + 1); } public: Syntax_check_handler() : m_depth(0) {} /* These functions are callbacks used by rapidjson::Reader when parsing a JSON document. They all follow the rapidjson convention of returning true on success and false on failure. */ bool StartObject() { return !check_json_depth(++m_depth); } bool EndObject(SizeType) { --m_depth; return true; } bool StartArray() { return !check_json_depth(++m_depth); } bool EndArray(SizeType) { --m_depth; return true; } bool Null() { return seeing_scalar(); } bool Bool(bool) { return seeing_scalar(); } bool Int(int) { return seeing_scalar(); } bool Uint(unsigned) { return seeing_scalar(); } bool Int64(int64_t) { return seeing_scalar(); } bool Uint64(uint64_t) { return seeing_scalar(); } bool Double(double, bool is_int= false) { return seeing_scalar(); } bool String(const char*, SizeType, bool) { return seeing_scalar(); } bool Key(const char*, SizeType, bool) { return seeing_scalar(); } bool RawNumber(const char*, SizeType, bool) { return seeing_scalar(); } }; bool is_valid_json_syntax(const char *text, size_t length) { Syntax_check_handler handler; Reader reader; MemoryStream ms(text, length); return reader.Parse<rapidjson::kParseDefaultFlags>(ms, handler); } /** Map the JSON type used by the binary representation to the type used by Json_dom and Json_wrapper. <p/> Note: Does not look into opaque values to determine if they represent decimal or date/time values. For that, look into the Value an retrive field_type. @param[in] bintype @returns the JSON_dom JSON type. */ static Json_dom::enum_json_type bjson2json(const json_binary::Value::enum_type bintype) { Json_dom::enum_json_type res= Json_dom::J_ERROR; switch (bintype) { case json_binary::Value::STRING: res= Json_dom::J_STRING; break; case json_binary::Value::INT: res= Json_dom::J_INT; break; case json_binary::Value::UINT: res= Json_dom::J_UINT; break; case json_binary::Value::DOUBLE: res= Json_dom::J_DOUBLE; break; case json_binary::Value::LITERAL_TRUE: case json_binary::Value::LITERAL_FALSE: res= Json_dom::J_BOOLEAN; break; case json_binary::Value::LITERAL_NULL: res= Json_dom::J_NULL; break; case json_binary::Value::ARRAY: res= Json_dom::J_ARRAY; break; case json_binary::Value::OBJECT: res= Json_dom::J_OBJECT; break; case json_binary::Value::ERROR: res= Json_dom::J_ERROR; break; case json_binary::Value::OPAQUE: res= Json_dom::J_OPAQUE; break; } return res; } Json_dom *Json_dom::parse(const json_binary::Value &v) { Json_dom *result= NULL; switch (v.type()) { case json_binary::Value::OBJECT: { std::auto_ptr<Json_object> jo(new (std::nothrow) Json_object()); if (jo.get() == NULL) return NULL; /* purecov: inspected */ for (uint32 i= 0; i < v.element_count(); ++i) { /* Add the key/value pair. Json_object::add_alias() guarantees that the value is deallocated if it cannot be added. */ if (jo->add_alias(std::string(v.key(i).get_data(), v.key(i).get_data_length()), parse(v.element(i)))) { return NULL; /* purecov: inspected */ } } result= jo.release(); break; } case json_binary::Value::ARRAY: { std::auto_ptr<Json_array> jarr(new (std::nothrow) Json_array()); if (jarr.get() == NULL) return NULL; /* purecov: inspected */ for (uint32 i= 0; i < v.element_count(); ++i) { /* Add the element to the array. We need to make sure it is deallocated if it cannot be added. std::auto_ptr does that for us. */ std::auto_ptr<Json_dom> elt(parse(v.element(i))); if (jarr->append_alias(elt.get())) return NULL; /* purecov: inspected */ // The array owns the element now. Release it. elt.release(); } result= jarr.release(); break; } case json_binary::Value::DOUBLE: result= new (std::nothrow) Json_double(v.get_double()); break; case json_binary::Value::INT: result= new (std::nothrow) Json_int(v.get_int64()); break; case json_binary::Value::UINT: result= new (std::nothrow) Json_uint(v.get_uint64()); break; case json_binary::Value::LITERAL_FALSE: result= new (std::nothrow) Json_boolean(false); break; case json_binary::Value::LITERAL_TRUE: result= new (std::nothrow) Json_boolean(true); break; case json_binary::Value::LITERAL_NULL: result= new (std::nothrow) Json_null(); break; case json_binary::Value::OPAQUE: { const enum_field_types ftyp= v.field_type(); if (ftyp == MYSQL_TYPE_NEWDECIMAL) { my_decimal m; if (Json_decimal::convert_from_binary(v.get_data(), v.get_data_length(), &m)) return NULL; /* purecov: inspected */ result= new (std::nothrow) Json_decimal(m); } else if (ftyp == MYSQL_TYPE_DATE || ftyp == MYSQL_TYPE_TIME || ftyp == MYSQL_TYPE_DATETIME || ftyp == MYSQL_TYPE_TIMESTAMP) { MYSQL_TIME t; Json_datetime::from_packed(v.get_data(), ftyp, &t); result= new (std::nothrow) Json_datetime(t, ftyp); } else { result= new (std::nothrow) Json_opaque(v.field_type(), v.get_data(), v.get_data_length()); } break; } case json_binary::Value::STRING: result= new (std::nothrow) Json_string(std::string(v.get_data(), v.get_data_length())); break; case json_binary::Value::ERROR: { /* purecov: begin inspected */ DBUG_ABORT(); my_error(ER_INVALID_JSON_BINARY_DATA, MYF(0)); break; /* purecov: end inspected */ } } return result; } void Json_array::replace_dom_in_container(Json_dom *oldv, Json_dom *newv) { Json_dom_vector::iterator it= std::find(m_v.begin(), m_v.end(), oldv); if (it != m_v.end()) { delete oldv; *it= newv; newv->set_parent(this); } } void Json_object::replace_dom_in_container(Json_dom *oldv, Json_dom *newv) { for (Json_object_map::iterator it= m_map.begin(); it != m_map.end(); ++it) { if (it->second == oldv) { delete oldv; it->second= newv; newv->set_parent(this); break; } } } bool Json_object::add_clone(const std::string &key, const Json_dom *value) { if (!value) return true; /* purecov: inspected */ return add_alias(key, value->clone()); } bool Json_object::add_alias(const std::string &key, Json_dom *value) { if (!value) return true; /* purecov: inspected */ /* Wrap value in an auto_ptr to make sure it's released if we cannot add it to the object. The contract of add_alias() requires that it either gets added to the object or gets deleted. */ std::auto_ptr<Json_dom> aptr(value); /* We have already an element with this key. Note we compare utf-8 bytes directly here. It's complicated when when you take into account composed and decomposed forms of accented characters and ligatures: different sequences might encode the same glyphs but we ignore that for now. For example, the code point U+006E (the Latin lowercase "n") followed by U+0303 (the combining tilde) is defined by Unicode to be canonically equivalent to the single code point U+00F1 (the lowercase letter of the Spanish alphabet). For now, users must normalize themselves to avoid element dups. This is what ECMAscript does also: "Two IdentifierName that are canonically equivalent according to the Unicode standard are not equal unless they are represented by the exact same sequence of code units (in other words, conforming ECMAScript implementations are only required to do bitwise comparison on IdentifierName values). The intent is that the incoming source text has been converted to normalised form C before it reaches the compiler." (ECMA-262 5.1 edition June 2011) See WL-2048 Add function for Unicode normalization */ std::pair<Json_object_map::const_iterator, bool> ret= m_map.insert(std::make_pair(key, value)); if (ret.second) { // the element was inserted value->set_parent(this); aptr.release(); } return false; } bool Json_object::consume(Json_object *other) { // We've promised to delete other before returning. std::auto_ptr<Json_object> aptr(other); Json_object_map &this_map= m_map; Json_object_map &other_map= other->m_map; for (Json_object_map::iterator other_iter= other_map.begin(); other_iter != other_map.end(); other_map.erase(other_iter++)) { const std::string &key= other_iter->first; Json_dom *value= other_iter->second; other_iter->second= NULL; Json_object_map::iterator this_iter= this_map.find(key); if (this_iter == this_map.end()) { // The key does not exist in this object, so add the key/value pair. if (add_alias(key, value)) return true; /* purecov: inspected */ } else { /* Oops. Duplicate key. Merge the values. This is where the recursion in JSON_MERGE() occurs. */ this_iter->second= merge_doms(this_iter->second, value); if (this_iter->second == NULL) return true; /* purecov: inspected */ this_iter->second->set_parent(this); } } return false; } Json_dom *Json_object::get(const std::string &key) const { const Json_object_map::const_iterator iter= m_map.find(key); if (iter != m_map.end()) { assert(iter->second->parent() == this); return iter->second; } return NULL; } bool Json_object::remove(const Json_dom *child) { for (Json_object_map::iterator iter= m_map.begin(); iter != m_map.end(); ++iter) { Json_dom *candidate= iter->second; if (child == candidate) { delete candidate; m_map.erase(iter); return true; } } // end of loop through children return false; } bool Json_object::remove(const std::string &key) { Json_object_map::iterator it= m_map.find(key); if (it == m_map.end()) return false; delete it->second; m_map.erase(it); return true; } size_t Json_object::cardinality() const { return m_map.size(); } uint32 Json_object::depth() const { uint deepest_child= 0; for (Json_object_map::const_iterator iter= m_map.begin(); iter != m_map.end(); ++iter) { deepest_child= std::max(deepest_child, iter->second->depth()); } return 1 + deepest_child; } Json_dom *Json_object::clone() const { Json_object * const o= new (std::nothrow) Json_object(); if (!o) return NULL; /* purecov: inspected */ for (Json_object_map::const_iterator iter= m_map.begin(); iter != m_map.end(); ++iter) { if (o->add_clone(iter->first, iter->second)) { delete o; /* purecov: inspected */ return NULL; /* purecov: inspected */ } } return o; } void Json_object::clear() { for (Json_object_map::const_iterator iter= m_map.begin(); iter != m_map.end(); ++iter) { delete iter->second; } m_map.clear(); } bool Json_object::merge_patch(Json_object *patch) { std::auto_ptr<Json_object> aptr(patch); // We own it, and must make sure // to delete it. for (Json_object_map::iterator it= patch->m_map.begin(); it != patch->m_map.end(); ++it) { const std::string &patch_key= it->first; std::auto_ptr<Json_dom> patch_value(it->second); it->second= NULL; // Remove the member if the value in the patch is the null literal. if (patch_value->json_type() == Json_dom::J_NULL) { remove(patch_key); continue; } // See if the target has this member, add it if not. std::pair<Json_object_map::iterator, bool> target_pair= m_map.insert(std::make_pair(patch_key, static_cast<Json_dom*>(NULL))); std::auto_ptr<Json_dom> target_value(target_pair.first->second); target_pair.first->second= NULL; /* If the value in the patch is not an object and not the null literal, the new value is the patch. */ if (patch_value->json_type() != Json_dom::J_OBJECT) { patch_value->set_parent(this); target_pair.first->second= patch_value.release(); continue; } /* If there is no target value, or if the target value is not an object, use an empty object as the target value. */ if (target_value.get() == NULL || target_value->json_type() != Json_dom::J_OBJECT) { target_value.reset(new (std::nothrow) Json_object()); } // Recursively merge the target value with the patch. Json_object *target_obj= down_cast<Json_object*>(target_value.get()); if (target_obj == NULL || target_obj->merge_patch(down_cast<Json_object*>(patch_value.release()))) return true; /* purecov: inspected */ target_value->set_parent(this); target_pair.first->second= target_value.release(); } return false; } /** Compare two keys from a JSON object and determine whether or not the first key is less than the second key. key1 is considered less than key2 if a) key1 is shorter than key2, or if b) key1 and key2 have the same length, but different contents, and the first byte that differs has a smaller value in key1 than in key2 Otherwise, key1 is not less than key2. @param key1 the first key to compare @param key2 the second key to compare @return true if key1 is considered less than key2, false otherwise */ bool Json_key_comparator::operator() (const std::string &key1, const std::string &key2) const { if (key1.length() != key2.length()) return key1.length() < key2.length(); return memcmp(key1.data(), key2.data(), key1.length()) < 0; } Json_array::Json_array() : Json_dom(), m_v(key_memory_JSON) {} Json_array::Json_array(Json_dom *innards) : Json_dom(), m_v(key_memory_JSON) { append_alias(innards); } Json_array::~Json_array() { delete_container_pointers(m_v); } bool Json_array::append_clone(const Json_dom *value) { if (!value) return true; /* purecov: inspected */ return append_alias(value->clone()); } bool Json_array::append_alias(Json_dom *value) { if (!value || m_v.push_back(value)) return true; /* purecov: inspected */ value->set_parent(this); return false; } bool Json_array::consume(Json_array *other) { // We've promised to delete other before returning. std::auto_ptr<Json_array> aptr(other); Json_dom_vector &other_vector= other->m_v; for (Json_dom_vector::iterator iter= other_vector.begin(); iter != other_vector.end(); ++iter) { if (append_alias(*iter)) return true; /* purecov: inspected */ *iter= NULL; } return false; } bool Json_array::insert_clone(size_t index, const Json_dom *value) { if (!value) return true; /* purecov: inspected */ return insert_alias(index, value->clone()); } bool Json_array::insert_alias(size_t index, Json_dom *value) { if (!value) return true; /* purecov: inspected */ Json_dom_vector::iterator iter= m_v.begin(); if (index < m_v.size()) { m_v.insert(iter + index, value); } else { //append needed if (m_v.push_back(value)) return true; /* purecov: inspected */ } value->set_parent(this); return false; } bool Json_array::remove(size_t index) { if (index < m_v.size()) { const Json_dom_vector::iterator iter= m_v.begin() + index; delete *iter; m_v.erase(iter); return true; } return false; } bool Json_array::remove(const Json_dom *child) { Json_dom_vector::iterator it= std::find(m_v.begin(), m_v.end(), child); if (it != m_v.end()) { delete child; m_v.erase(it); return true; } return false; } uint32 Json_array::depth() const { uint deepest_child= 0; for (Json_dom_vector::const_iterator it= m_v.begin(); it != m_v.end(); ++it) { deepest_child= std::max(deepest_child, (*it)->depth()); } return 1 + deepest_child; } Json_dom *Json_array::clone() const { Json_array * const vv= new (std::nothrow) Json_array(); if (!vv) return NULL; /* purecov: inspected */ for (Json_dom_vector::const_iterator it= m_v.begin(); it != m_v.end(); ++it) { if (vv->append_clone(*it)) { delete vv; /* purecov: inspected */ return NULL; /* purecov: inspected */ } } return vv; } void Json_array::clear() { delete_container_pointers(m_v); } /** Reserve space in a string buffer. If reallocation is needed, increase the size of the buffer exponentially. @param buffer the string buffer @param needed the number of bytes needed @return true on error, false on success */ static bool reserve(String *buffer, size_t needed) { return buffer->reserve(needed, buffer->length()); } /** Perform quoting on a JSON string to make an external representation of it. it wraps double quotes (text quotes) around the string (cptr) an also performs escaping according to the following table: <pre> Common name C-style Original unescaped Transformed to escape UTF-8 bytes escape sequence notation in UTF-8 bytes --------------------------------------------------------------- quote \" %x22 %x5C %x22 backslash \\ %x5C %x5C %x5C backspace \b %x08 %x5C %x62 formfeed \f %x0C %x5C %x66 linefeed \n %x0A %x5C %x6E carriage-return \r %x0D %x5C %x72 tab \t %x09 %x5C %x74 unicode \uXXXX A hex number in the %x5C %x75 range of 00-1F, followed by except for the ones 4 hex digits handled above (backspace, formfeed, linefeed, carriage-return, and tab). --------------------------------------------------------------- </pre> @param[in] cptr pointer to string data @param[in] length the length of the string @param[in,out] buf the destination buffer @retval false on success @retval true on error */ bool double_quote(const char *cptr, size_t length, String *buf) { if (reserve(buf, length + 2) || buf->append('"')) return true; /* purecov: inspected */ for (size_t i= 0; i < length; i++) { char esc[2]= {'\\', cptr[i]}; bool done= true; switch (cptr[i]) { case '"' : case '\\' : break; case '\b': esc[1]= 'b'; break; case '\f': esc[1]= 'f'; break; case '\n': esc[1]= 'n'; break; case '\r': esc[1]= 'r'; break; case '\t': esc[1]= 't'; break; default: done= false; } if (done) { if (buf->append(esc[0]) || buf->append(esc[1])) return true; /* purecov: inspected */ } else if (((cptr[i] & ~0x7f) == 0) && // bit 8 not set (cptr[i] <= 0x1f)) { /* Unprintable control character, use hex a hexadecimal number. The meaning of such a number determined by ISO/IEC 10646. */ if (buf->append("\\u00") || buf->append(_dig_vec_lower[(cptr[i] & 0xf0) >> 4]) || buf->append(_dig_vec_lower[(cptr[i] & 0x0f)])) return true; /* purecov: inspected */ } else if (buf->append(cptr[i])) { return true; /* purecov: inspected */ } } return buf->append('"'); } Json_decimal::Json_decimal(const my_decimal &value) : Json_number(), m_dec(value) {} int Json_decimal::binary_size() const { /* We need two bytes for the precision and the scale, plus whatever my_decimal2binary() needs. */ return 2 + my_decimal_get_binary_size(m_dec.precision(), m_dec.frac); } bool Json_decimal::get_binary(char* dest) const { assert(binary_size() <= MAX_BINARY_SIZE); /* my_decimal2binary() loses the precision and the scale, so store them in the first two bytes. */ dest[0]= static_cast<char>(m_dec.precision()); dest[1]= static_cast<char>(m_dec.frac); // Then store the decimal value. return my_decimal2binary(E_DEC_ERROR, &m_dec, pointer_cast<uchar*>(dest) + 2, m_dec.precision(), m_dec.frac) != E_DEC_OK; } bool Json_decimal::convert_from_binary(const char *bin, size_t len, my_decimal *dec) { // Expect at least two bytes, which contain precision and scale. bool error= (len < 2); if (!error) { int precision= bin[0]; int scale= bin[1]; // The decimal value is encoded after the two precision/scale bytes. size_t bin_size= my_decimal_get_binary_size(precision, scale); error= (bin_size != len - 2) || (binary2my_decimal(E_DEC_ERROR, pointer_cast<const uchar*>(bin) + 2, dec, precision, scale) != E_DEC_OK); } if (error) my_error(ER_INVALID_JSON_BINARY_DATA, MYF(0)); /* purecov: inspected */ return error; } Json_dom *Json_double::clone() const { return new (std::nothrow) Json_double(m_f); } Json_dom::enum_json_type Json_datetime::json_type () const { switch (m_field_type) { case MYSQL_TYPE_TIME : return J_TIME; case MYSQL_TYPE_DATETIME: return J_DATETIME; case MYSQL_TYPE_DATE: return J_DATE; case MYSQL_TYPE_TIMESTAMP: return J_TIMESTAMP; default: ; } /* purecov: begin inspected */ DBUG_ABORT(); return J_NULL; /* purecov: end inspected */ } Json_dom *Json_datetime::clone() const { return new (std::nothrow) Json_datetime(m_t, m_field_type); } void Json_datetime::to_packed(char *dest) const { longlong packed= TIME_to_longlong_packed(&m_t); int8store(dest, packed); } void Json_datetime::from_packed(const char *from, enum_field_types ft, MYSQL_TIME *to) { TIME_from_longlong_packed(to, ft, sint8korr(from)); } Json_opaque::Json_opaque(enum_field_types mytype, const char *v, size_t size) : Json_scalar(), m_mytype(mytype), m_val(v, size) {} Json_dom *Json_opaque::clone() const { return new (std::nothrow) Json_opaque(m_mytype, value(), size()); } Json_wrapper_object_iterator:: Json_wrapper_object_iterator(const Json_object *obj) : m_is_dom(true), m_iter(obj->begin()), m_end(obj->end()), m_element_count(-1) {} Json_wrapper_object_iterator:: Json_wrapper_object_iterator(const json_binary::Value *value) : m_is_dom(false), m_element_count(value->element_count()), m_value(value) { m_curr_element= 0; } bool Json_wrapper_object_iterator::empty() const { return m_is_dom ? (m_iter == m_end) : (m_curr_element >= m_element_count); } void Json_wrapper_object_iterator::next() { if (m_is_dom) { m_iter++; } else { ++m_curr_element; } } std::pair<const std::string, Json_wrapper> Json_wrapper_object_iterator::elt() const { if (m_is_dom) { Json_wrapper wr(m_iter->second); // DOM possibly owned by object and we don't want to make a clone wr.set_alias(); return std::make_pair(m_iter->first, wr); } std::string key(m_value->key(m_curr_element).get_data(), m_value->key(m_curr_element).get_data_length()); Json_wrapper wr(m_value->element(m_curr_element)); return std::make_pair(key, wr); } Json_wrapper::Json_wrapper(Json_dom *dom_value) : m_is_dom(true), m_dom_alias(false), m_value(), m_dom_value(dom_value) { if (!dom_value) { m_dom_alias= true; //!< no deallocation, make us empty } } void Json_wrapper::steal(Json_wrapper *old) { if (old->m_is_dom) { bool old_is_aliased= old->m_dom_alias; old->m_dom_alias= true; // we want no deep copy now, or later *this= *old; this->m_dom_alias= old_is_aliased; // set it back // old is now marked as aliased, so any ownership is effectively // transferred to this. } else { *this= *old; } } Json_wrapper::Json_wrapper(const json_binary::Value &value) : m_is_dom(false), m_dom_alias(false), m_value(value), m_dom_value(NULL) {} Json_wrapper::Json_wrapper(const Json_wrapper &old) : m_is_dom(old.m_is_dom), m_dom_alias(old.m_dom_alias), m_value(old.m_value), m_dom_value(old.m_is_dom ? (m_dom_alias? old.m_dom_value : old.m_dom_value->clone()) : NULL) {} Json_wrapper::~Json_wrapper() { if (m_is_dom && !m_dom_alias) { // we own our own copy, so we are responsible for deallocation delete m_dom_value; } } Json_wrapper &Json_wrapper::operator=(const Json_wrapper& from) { if (this == &from) { return *this; // self assignment: no-op } if (m_is_dom && !m_dom_alias &&!empty()) { // we own our own copy, so we are responsible for deallocation delete m_dom_value; } m_is_dom= from.m_is_dom; if (from.m_is_dom) { if (from.m_dom_alias) { m_dom_value= from.m_dom_value; } else { m_dom_value= from.m_dom_value->clone(); } m_dom_alias= from.m_dom_alias; } else { m_dom_value= NULL; m_value= from.m_value; } return *this; } Json_dom *Json_wrapper::to_dom() { if (!m_is_dom) { // Build a DOM from the binary JSON value and // convert this wrapper to hold the DOM instead m_dom_value= Json_dom::parse(m_value); m_is_dom= true; m_dom_alias= false; } return m_dom_value; } Json_dom *Json_wrapper::clone_dom() { // If we already have a DOM, return a clone of it. if (m_is_dom) return m_dom_value ? m_dom_value->clone() : NULL; // Otherwise, produce a new DOM tree from the binary representation. return Json_dom::parse(m_value); } bool Json_wrapper::to_binary(String *str) const { if (empty()) { /* purecov: begin inspected */ my_error(ER_INVALID_JSON_BINARY_DATA, MYF(0)); return true; /* purecov: end */ } if (m_is_dom) return json_binary::serialize(m_dom_value, str); return m_value.raw_binary(str); } /** Possibly append a single quote to a buffer. @param[in,out] buffer receiving buffer @param[in] json_quoted whether or not a quote should be appended @return false if successful, true on error */ inline bool single_quote(String *buffer, bool json_quoted) { return json_quoted && buffer->append('"'); } /** Pretty-print a string to an evolving buffer, double-quoting if requested. @param[in] buffer the buffer to print to @param[in] json_quoted true if we should double-quote @param[in] data the string to print @param[in] length the string's length @return false on success, true on failure */ static int print_string(String *buffer, bool json_quoted, const char *data, size_t length) { return json_quoted ? double_quote(data, length, buffer) : buffer->append(data, length); } /** Helper function for wrapper_to_string() which adds a newline and indentation up to the specified level. @param[in,out] buffer the buffer to write to @param[in] level how many nesting levels to add indentation for @retval false on success @retval true on error */ static bool newline_and_indent(String *buffer, size_t level) { // Append newline and two spaces per indentation level. return buffer->append('\n') || buffer->fill(buffer->length() + level * 2, ' '); } /** Append a comma to separate elements in JSON arrays and objects. @param buffer the string buffer @param pretty true if pretty printing is enabled @return true on error, false on success */ static bool append_comma(String *buffer, bool pretty) { // Append a comma followed by a blank space. If pretty printing is // enabled, a newline will be added in front of the next element, so // the blank space can be omitted. return buffer->append(',') || (!pretty && buffer->append(' ')); } /** Helper function which does all the heavy lifting for Json_wrapper::to_string(). It processes the Json_wrapper recursively. The depth parameter keeps track of the current nesting level. When it reaches JSON_DOCUMENT_MAX_DEPTH, it gives up in order to avoid running out of stack space. @param[in] wr the value to convert to a string @param[in,out] buffer the buffer to write to @param[in] json_quoted quote strings if true @param[in] pretty add newlines and indentation if true @param[in] func_name the name of the calling function @param[in] depth the nesting level of @a wr @retval false on success @retval true on error */ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, bool json_quoted, bool pretty, const char *func_name, size_t depth) { if (check_json_depth(++depth)) return true; switch (wr.type()) { case Json_dom::J_TIME: case Json_dom::J_DATE: case Json_dom::J_DATETIME: case Json_dom::J_TIMESTAMP: { // Make sure the buffer has space for the datetime and the quotes. if (reserve(buffer, MAX_DATE_STRING_REP_LENGTH + 2)) return true; /* purecov: inspected */ MYSQL_TIME t; wr.get_datetime(&t); if (single_quote(buffer, json_quoted)) return true; /* purecov: inspected */ char *ptr= const_cast<char *>(buffer->ptr()) + buffer->length(); const int size= my_TIME_to_str(&t, ptr, 6); buffer->length(buffer->length() + size); if (single_quote(buffer, json_quoted)) return true; /* purecov: inspected */ break; } case Json_dom::J_ARRAY: { if (buffer->append('[')) return true; /* purecov: inspected */ size_t array_len= wr.length(); for (uint32 i= 0; i < array_len; ++i) { if (i > 0 && append_comma(buffer, pretty)) return true; /* purecov: inspected */ if (pretty && newline_and_indent(buffer, depth)) return true; /* purecov: inspected */ if (wrapper_to_string(wr[i], buffer, true, pretty, func_name, depth)) return true; /* purecov: inspected */ } if (pretty && array_len > 0 && newline_and_indent(buffer, depth - 1)) return true; /* purecov: inspected */ if (buffer->append(']')) return true; /* purecov: inspected */ break; } case Json_dom::J_BOOLEAN: if (wr.get_boolean() ? buffer->append(STRING_WITH_LEN("true")) : buffer->append(STRING_WITH_LEN("false"))) return true; /* purecov: inspected */ break; case Json_dom::J_DECIMAL: { int length= DECIMAL_MAX_STR_LENGTH + 1; if (reserve(buffer, length)) return true; char *ptr= const_cast<char *>(buffer->ptr()) + buffer->length(); my_decimal m; if (wr.get_decimal_data(&m) || decimal2string(&m, ptr, &length, 0, 0, 0)) return true; /* purecov: inspected */ buffer->length(buffer->length() + length); break; } case Json_dom::J_DOUBLE: { if (reserve(buffer, MY_GCVT_MAX_FIELD_WIDTH + 1)) return true; /* purecov: inspected */ double d= wr.get_double(); size_t len= my_gcvt(d, MY_GCVT_ARG_DOUBLE, MY_GCVT_MAX_FIELD_WIDTH, const_cast<char *>(buffer->ptr()) + buffer->length(), NULL); buffer->length(buffer->length() + len); break; } case Json_dom::J_INT: { if (buffer->append_longlong(wr.get_int())) return true; /* purecov: inspected */ break; } case Json_dom::J_NULL: if (buffer->append(STRING_WITH_LEN("null"))) return true; /* purecov: inspected */ break; case Json_dom::J_OBJECT: { if (buffer->append('{')) return true; /* purecov: inspected */ bool first= true; for (Json_wrapper_object_iterator iter= wr.object_iterator(); !iter.empty(); iter.next()) { if (!first && append_comma(buffer, pretty)) return true; /* purecov: inspected */ first= false; if (pretty && newline_and_indent(buffer, depth)) return true; /* purecov: inspected */ const std::string &key= iter.elt().first; const char *key_data= key.c_str(); size_t key_length= key.length(); if (print_string(buffer, true, key_data, key_length) || buffer->append(':') || buffer->append(' ') || wrapper_to_string(iter.elt().second, buffer, true, pretty, func_name, depth)) return true; /* purecov: inspected */ } if (pretty && wr.length() > 0 && newline_and_indent(buffer, depth - 1)) return true; /* purecov: inspected */ if (buffer->append('}')) return true; /* purecov: inspected */ break; } case Json_dom::J_OPAQUE: { if (wr.get_data_length() > base64_encode_max_arg_length()) { /* purecov: begin inspected */ my_error(ER_INTERNAL_ERROR, MYF(0), "JSON: could not decode opaque data"); return true; /* purecov: end */ } const size_t needed= static_cast<size_t>(base64_needed_encoded_length(wr.get_data_length())); if (single_quote(buffer, json_quoted) || buffer->append(STRING_WITH_LEN("base64:type")) || buffer->append_ulonglong(wr.field_type()) || buffer->append(':')) return true; /* purecov: inspected */ // "base64:typeXX:<binary data>" size_t pos= buffer->length(); if (reserve(buffer, needed) || base64_encode(wr.get_data(), wr.get_data_length(), const_cast<char*>(buffer->ptr() + pos))) return true; /* purecov: inspected */ buffer->length(pos + needed - 1); // drop zero terminator space if (single_quote(buffer, json_quoted)) return true; /* purecov: inspected */ break; } case Json_dom::J_STRING: { const char *data= wr.get_data(); size_t length= wr.get_data_length(); if (print_string(buffer, json_quoted, data, length)) return true; /* purecov: inspected */ break; } case Json_dom::J_UINT: { if (buffer->append_ulonglong(wr.get_uint())) return true; /* purecov: inspected */ break; } default: /* purecov: begin inspected */ DBUG_ABORT(); my_error(ER_INTERNAL_ERROR, MYF(0), "JSON wrapper: unexpected type"); return true; /* purecov: end inspected */ } if (buffer->length() > current_thd->variables.max_allowed_packet) { push_warning_printf(current_thd, Sql_condition::SL_WARNING, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER_THD(current_thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name, current_thd->variables.max_allowed_packet); return true; } return false; } bool Json_wrapper::to_string(String *buffer, bool json_quoted, const char *func_name) const { buffer->set_charset(&my_charset_utf8mb4_bin); return wrapper_to_string(*this, buffer, json_quoted, false, func_name, 0); } bool Json_wrapper::to_pretty_string(String *buffer, const char *func_name) const { buffer->set_charset(&my_charset_utf8mb4_bin); return wrapper_to_string(*this, buffer, true, true, func_name, 0); } Json_dom::enum_json_type Json_wrapper::type() const { if (empty()) { return Json_dom::J_ERROR; } if (m_is_dom) { return m_dom_value->json_type(); } json_binary::Value::enum_type typ= m_value.type(); if (typ == json_binary::Value::OPAQUE) { const enum_field_types ftyp= m_value.field_type(); switch (ftyp) { case MYSQL_TYPE_NEWDECIMAL: return Json_dom::J_DECIMAL; case MYSQL_TYPE_DATETIME: return Json_dom::J_DATETIME; case MYSQL_TYPE_DATE: return Json_dom::J_DATE; case MYSQL_TYPE_TIME: return Json_dom::J_TIME; case MYSQL_TYPE_TIMESTAMP: return Json_dom::J_TIMESTAMP; default: ; // ok, fall through } } return bjson2json(typ); } enum_field_types Json_wrapper::field_type() const { if (m_is_dom) { return down_cast<Json_opaque *>(m_dom_value)->type(); } return m_value.field_type(); } Json_wrapper_object_iterator Json_wrapper::object_iterator() const { assert(type() == Json_dom::J_OBJECT); if (m_is_dom) { const Json_object *o= down_cast<const Json_object *>(m_dom_value); return Json_wrapper_object_iterator(o); } return Json_wrapper_object_iterator(&m_value); } Json_wrapper Json_wrapper::lookup(const char *key, size_t len) const { assert(type() == Json_dom::J_OBJECT); if (m_is_dom) { const Json_object *object= down_cast<const Json_object *>(m_dom_value); std::string member_name(key, len); Json_wrapper wr(object->get(member_name)); wr.set_alias(); // wr doesn't own the supplied DOM: part of array DOM return wr; } return Json_wrapper(m_value.lookup(key, len)); } Json_wrapper Json_wrapper::operator[](size_t index) const { assert(type() == Json_dom::J_ARRAY); if (m_is_dom) { const Json_array *o= down_cast<const Json_array *>(m_dom_value); Json_wrapper wr((*o)[index]); wr.set_alias(); // wr doesn't own the supplied DOM: part of array DOM return wr; } return Json_wrapper(m_value.element(index)); } const char *Json_wrapper::get_data() const { if (m_is_dom) { return type() == Json_dom::J_STRING ? down_cast<Json_string *>(m_dom_value)->value().c_str() : down_cast<Json_opaque *>(m_dom_value)->value(); } return m_value.get_data(); } size_t Json_wrapper::get_data_length() const { if (m_is_dom) { return type() == Json_dom::J_STRING ? down_cast<Json_string *>(m_dom_value)->size() : down_cast<Json_opaque *>(m_dom_value)->size(); } return m_value.get_data_length(); } bool Json_wrapper::get_decimal_data(my_decimal *d) const { if (m_is_dom) { *d= *down_cast<Json_decimal *>(m_dom_value)->value(); return false; } return Json_decimal::convert_from_binary(m_value.get_data(), m_value.get_data_length(), d); } double Json_wrapper::get_double() const { if (m_is_dom) { return down_cast<Json_double *>(m_dom_value)->value(); } return m_value.get_double(); } longlong Json_wrapper::get_int() const { if (m_is_dom) { return down_cast<Json_int *>(m_dom_value)->value(); } return m_value.get_int64(); } ulonglong Json_wrapper::get_uint() const { if (m_is_dom) { return down_cast<Json_uint *>(m_dom_value)->value(); } return m_value.get_uint64(); } void Json_wrapper::get_datetime(MYSQL_TIME *t) const { enum_field_types ftyp= MYSQL_TYPE_NULL; switch(type()) { case Json_dom::J_DATE: ftyp= MYSQL_TYPE_DATE; break; case Json_dom::J_DATETIME: case Json_dom::J_TIMESTAMP: ftyp= MYSQL_TYPE_DATETIME; break; case Json_dom::J_TIME: ftyp= MYSQL_TYPE_TIME; break; default: DBUG_ABORT(); /* purecov: inspected */ } if (m_is_dom) { *t= *down_cast<Json_datetime *>(m_dom_value)->value(); } else { Json_datetime::from_packed(m_value.get_data(), ftyp, t); } } const char *Json_wrapper::get_datetime_packed(char *buffer) const { if (m_is_dom) { down_cast<Json_datetime *>(m_dom_value)->to_packed(buffer); return buffer; } assert(m_value.get_data_length() == Json_datetime::PACKED_SIZE); return m_value.get_data(); } bool Json_wrapper::get_boolean() const { if (m_is_dom) { return down_cast<Json_boolean *>(m_dom_value)->value(); } return m_value.type() == json_binary::Value::LITERAL_TRUE; } Json_path Json_dom::get_location() { if (m_parent == NULL) { Json_path result; return result; } Json_path result= m_parent->get_location(); if (m_parent->json_type() == Json_dom::J_OBJECT) { Json_object *object= down_cast<Json_object *>(m_parent); for (Json_object::const_iterator it= object->begin(); it != object->end(); ++it) { if (it->second == this) { Json_path_leg child_leg(it->first); result.append(child_leg); break; } } } else { assert(m_parent->json_type() == Json_dom::J_ARRAY); Json_array *array= down_cast<Json_array *>(m_parent); for (size_t idx= 0; idx < array->size(); idx++) { if ((*array)[idx] == this) { Json_path_leg child_leg(idx); result.append(child_leg); break; } } } return result; } bool Json_dom::seek(const Json_seekable_path &path, Json_dom_vector *hits, bool auto_wrap, bool only_need_one) { Json_dom_vector candidates(key_memory_JSON); Json_dom_vector duplicates(key_memory_JSON); if (hits->push_back(this)) return true; /* purecov: inspected */ size_t path_leg_count= path.leg_count(); for (size_t path_idx= 0; path_idx < path_leg_count; path_idx++) { const Json_path_leg *path_leg= path.get_leg_at(path_idx); duplicates.clear(); candidates.clear(); for (Json_dom_vector::iterator it= hits->begin(); it != hits->end(); ++it) { if ((*it)->find_child_doms(path_leg, auto_wrap, (only_need_one && (path_idx == (path_leg_count-1))), &duplicates, &candidates)) return true; /* purecov: inspected */ } // swap the two lists so that they can be re-used hits->swap(candidates); } return false; } bool Json_wrapper::seek_no_ellipsis(const Json_seekable_path &path, Json_wrapper_vector *hits, const size_t leg_number, bool auto_wrap, bool only_need_one) const { if (leg_number >= path.leg_count()) { if (m_is_dom) { Json_wrapper clone(m_dom_value->clone()); if (clone.empty() || hits->push_back(Json_wrapper())) return true; /* purecov: inspected */ hits->back().steal(&clone); return false; } return hits->push_back(*this); } const Json_path_leg *path_leg= path.get_leg_at(leg_number); switch(path_leg->get_type()) { case jpl_member: { switch(this->type()) { case Json_dom::J_OBJECT: { const char *key= path_leg->get_member_name(); size_t key_length= path_leg->get_member_name_length(); Json_wrapper member= lookup(key, key_length); if (!member.empty() & !(member.type() == Json_dom::J_ERROR)) { // recursion if (member.seek_no_ellipsis(path, hits, leg_number + 1, auto_wrap, only_need_one)) return true; /* purecov: inspected */ } return false; } default: { return false; } } // end inner switch on wrapper type } case jpl_member_wildcard: { switch(this->type()) { case Json_dom::J_OBJECT: { for (Json_wrapper_object_iterator iter= object_iterator(); !iter.empty(); iter.next()) { if (is_seek_done(hits, only_need_one)) return false; // recursion if (iter.elt().second.seek_no_ellipsis(path, hits, leg_number + 1, auto_wrap, only_need_one)) return true; /* purecov: inspected */ } return false; } default: { return false; } } // end inner switch on wrapper type } case jpl_array_cell: { size_t cell_idx= path_leg->get_array_cell_index(); // handle auto-wrapping if ((cell_idx == 0) && auto_wrap && (this->type() != Json_dom::J_ARRAY)) { // recursion return seek_no_ellipsis(path, hits, leg_number + 1, auto_wrap, only_need_one); } switch(this->type()) { case Json_dom::J_ARRAY: { if (cell_idx < this->length()) { Json_wrapper cell= (*this)[cell_idx]; return cell.seek_no_ellipsis(path, hits, leg_number + 1, auto_wrap, only_need_one); } return false; } default: { return false; } } // end inner switch on wrapper type } case jpl_array_cell_wildcard: { switch(this->type()) { case Json_dom::J_ARRAY: { size_t array_length= this->length(); for (size_t idx= 0; idx < array_length; idx++) { if (is_seek_done(hits, only_need_one)) return false; // recursion Json_wrapper cell= (*this)[idx]; if (cell.seek_no_ellipsis(path, hits, leg_number + 1, auto_wrap, only_need_one)) return true; /* purecov: inspected */ } return false; } default: { return false; } } // end inner switch on wrapper type } default: // should never be called on a path which contains an ellipsis DBUG_ABORT(); /* purecov: inspected */ return true; /* purecov: inspected */ } // end outer switch on leg type } bool Json_wrapper::seek(const Json_seekable_path &path, Json_wrapper_vector *hits, bool auto_wrap, bool only_need_one) { if (empty()) { /* purecov: begin inspected */ DBUG_ABORT(); return false; /* purecov: end */ } // use fast-track code if the path doesn't have any ellipses if (!path.contains_ellipsis()) { return seek_no_ellipsis(path, hits, 0, auto_wrap, only_need_one); } /* FIXME. Materialize the dom if the path contains ellipses. Duplicate detection is difficult on binary values. */ to_dom(); Json_dom_vector dhits(key_memory_JSON); if (m_dom_value->seek(path, &dhits, auto_wrap, only_need_one)) return true; /* purecov: inspected */ for (Json_dom_vector::iterator it= dhits.begin(); it != dhits.end(); ++it) { Json_wrapper clone((*it)->clone()); if (clone.empty() || hits->push_back(Json_wrapper())) return true; /* purecov: inspected */ hits->back().steal(&clone); } return false; } size_t Json_wrapper::length() const { if (empty()) { return 0; } if (m_is_dom) { switch(m_dom_value->json_type()) { case Json_dom::J_ARRAY: return down_cast<Json_array *>(m_dom_value)->size(); case Json_dom::J_OBJECT: return down_cast<Json_object *>(m_dom_value)->cardinality(); default: return 1; } } switch(m_value.type()) { case json_binary::Value::ARRAY: case json_binary::Value::OBJECT: return m_value.element_count(); default: return 1; } } size_t Json_wrapper::depth() const { if (empty()) { return 0; } if (m_is_dom) { return m_dom_value->depth(); } Json_dom *d= Json_dom::parse(m_value); size_t result= d->depth(); delete d; return result; } /** Compare two numbers of the same type. @param val1 the first number @param val2 the second number @retval -1 if val1 is less than val2, @retval 0 if val1 is equal to val2, @retval 1 if val1 is greater than val2 */ template <class T> static int compare_numbers(T val1, T val2) { return (val1 < val2) ? -1 : ((val1 == val2) ? 0 : 1); } /** Compare a decimal value to a double by converting the double to a decimal. @param a the decimal value @param b the double value @return -1 if a is less than b, 0 if a is equal to b, 1 if a is greater than b */ static int compare_json_decimal_double(const my_decimal &a, double b) { /* First check the sign of the two values. If they differ, the negative value is the smaller one. */ const bool a_is_zero= my_decimal_is_zero(&a); const bool a_is_negative= a.sign() && !a_is_zero; const bool b_is_negative= (b < 0); if (a_is_negative != b_is_negative) return a_is_negative ? -1 : 1; // Both arguments have the same sign. Compare their values. const bool b_is_zero= b == 0; if (a_is_zero) // b is non-negative, so it is either equal to or greater than a. return b_is_zero ? 0 : -1; if (b_is_zero) // a is positive and non-zero, so it is greater than b. return 1; my_decimal b_dec; switch (double2decimal(b, &b_dec)) { case E_DEC_OK: return my_decimal_cmp(&a, &b_dec); case E_DEC_OVERFLOW: /* b is too big to fit in a DECIMAL, so it must have a larger absolute value than a, which is a DECIMAL. */ return a_is_negative ? 1 : -1; case E_DEC_TRUNCATED: /* b was truncated to fit in a DECIMAL, which means that b_dec is closer to zero than b. */ { int cmp= my_decimal_cmp(&a, &b_dec); /* If the truncated b_dec is equal to a, a must be closer to zero than b. */ if (cmp == 0) return a_is_negative ? 1 : -1; return cmp; } default: /* double2decimal() is not supposed to return anything other than E_DEC_OK, E_DEC_OVERFLOW or E_DEC_TRUNCATED, so this should never happen. */ DBUG_ABORT(); /* purecov: inspected */ return 1; /* purecov: inspected */ } } /** Compare a decimal value to a signed integer by converting the integer to a decimal. @param a the decimal value @param b the signed integer value @return -1 if a is less than b, 0 if a is equal to b, 1 if a is greater than b */ static int compare_json_decimal_int(const my_decimal &a, longlong b) { if (my_decimal_is_zero(&a)) return (b == 0) ? 0 : (b > 0 ? -1 : 1); if (b == 0) return a.sign() ? -1 : 1; // Different signs. The negative number is the smallest one. if (a.sign() != (b < 0)) return (b < 0) ? 1 : -1; // Couldn't tell the difference by looking at the signs. Compare as decimals. my_decimal b_dec; longlong2decimal(b, &b_dec); return my_decimal_cmp(&a, &b_dec); } /** Compare a decimal value to an unsigned integer by converting the integer to a decimal. @param a the decimal value @param b the unsigned integer value @return -1 if a is less than b, 0 if a is equal to b, 1 if a is greater than b */ static int compare_json_decimal_uint(const my_decimal &a, ulonglong b) { if (my_decimal_is_zero(&a)) return (b == 0) ? 0 : -1; // If a is negative, it must be smaller than the unsigned value b. if (a.sign()) return -1; // When we get here, we know that a is greater than zero. if (b == 0) return 1; // Couldn't tell the difference by looking at the signs. Compare as decimals. my_decimal b_dec; ulonglong2decimal(b, &b_dec); return my_decimal_cmp(&a, &b_dec); } /** Compare a JSON double to a JSON signed integer. @param a the double value @param b the integer value @return -1 if a is less than b, 0 if a is equal to b, 1 if a is greater than b */ static int compare_json_double_int(double a, longlong b) { double b_double= static_cast<double>(b); if (a < b_double) return -1; if (a > b_double) return 1; /* The two numbers were equal when compared as double. Since conversion from longlong to double isn't lossless, they could still be different. Convert to decimal to compare their exact values. */ my_decimal b_dec; longlong2decimal(b, &b_dec); return -compare_json_decimal_double(b_dec, a); } /** Compare a JSON double to a JSON unsigned integer. @param a the double value @param b the unsigned integer value @return -1 if a is less than b, 0 if a is equal to b, 1 if a is greater than b */ static int compare_json_double_uint(double a, ulonglong b) { double b_double= ulonglong2double(b); if (a < b_double) return -1; if (a > b_double) return 1; /* The two numbers were equal when compared as double. Since conversion from longlong to double isn't lossless, they could still be different. Convert to decimal to compare their exact values. */ my_decimal b_dec; ulonglong2decimal(b, &b_dec); return -compare_json_decimal_double(b_dec, a); } /** Compare a JSON signed integer to a JSON unsigned integer. @param a the signed integer @param b the unsigned integer @return -1 if a is less than b, 0 if a is equal to b, 1 if a is greater than b */ static int compare_json_int_uint(longlong a, ulonglong b) { // All negative values are less than the unsigned value b. if (a < 0) return -1; // If a is not negative, it is safe to cast it to ulonglong. return compare_numbers(static_cast<ulonglong>(a), b); } /** Compare the contents of two strings in a JSON value. The strings could be either JSON string scalars encoded in utf8mb4, or binary strings from JSON opaque scalars. In either case they are compared byte by byte. @param str1 the first string @param str1_len the length of str1 @param str2 the second string @param str2_len the length of str2 @retval -1 if str1 is less than str2, @retval 0 if str1 is equal to str2, @retval 1 if str1 is greater than str2 */ static int compare_json_strings(const char *str1, size_t str1_len, const char *str2, size_t str2_len) { int cmp= memcmp(str1, str2, std::min(str1_len, str2_len)); if (cmp != 0) return cmp; return compare_numbers(str1_len, str2_len); } /** The following matrix tells how two JSON values should be compared based on their types. If type_comparison[type_of_a][type_of_b] is -1, it means that a is smaller than b. If it is 1, it means that a is greater than b. If it is 0, it means it cannot be determined which value is the greater one just by looking at the types. */ static const int type_comparison[Json_dom::J_ERROR + 1][Json_dom::J_ERROR + 1]= { /* NULL */ {0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* DECIMAL */ {1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* INT */ {1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* UINT */ {1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* DOUBLE */ {1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* STRING */ {1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* OBJECT */ {1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1}, /* ARRAY */ {1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1}, /* BOOLEAN */ {1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1}, /* DATE */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1}, /* TIME */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1}, /* DATETIME */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, -1, -1}, /* TIMESTAMP */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, -1, -1}, /* OPAQUE */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, -1}, /* ERROR */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }; int Json_wrapper::compare(const Json_wrapper &other) const { const Json_dom::enum_json_type this_type= type(); const Json_dom::enum_json_type other_type= other.type(); assert(this_type != Json_dom::J_ERROR); assert(other_type != Json_dom::J_ERROR); // Check if the type tells us which value is bigger. int cmp= type_comparison[this_type][other_type]; if (cmp != 0) return cmp; // Same or similar type. Go on and inspect the values. switch (this_type) { case Json_dom::J_ARRAY: /* Two arrays are equal if they have the same length, and all elements in one array are equal to the corresponding elements in the other array. The array that has the smallest value on the first position that contains different values in the two arrays, is considered smaller than the other array. If the two arrays are of different size, and all values in the shorter array are equal to the corresponding values in the longer array, the shorter array is considered smaller. */ { const size_t size_a= length(); const size_t size_b= other.length(); const size_t min_size= std::min(size_a, size_b); for (size_t i= 0; i < min_size; i++) { int cmp= (*this)[i].compare(other[i]); if (cmp != 0) return cmp; } return compare_numbers(size_a, size_b); } case Json_dom::J_OBJECT: /* An object is equal to another object if they have the same set of keys, and all values in one objects are equal to the values associated with the same key in the other object. */ { /* If their sizes are different, the object with the smallest number of elements is smaller than the other object. */ cmp= compare_numbers(length(), other.length()); if (cmp != 0) return cmp; /* Otherwise, compare each key/value pair in the two objects. Return on the first difference that is found. */ Json_wrapper_object_iterator it1= object_iterator(); Json_wrapper_object_iterator it2= other.object_iterator(); while (!it1.empty()) { const std::pair<const std::string, Json_wrapper> elt1= it1.elt(); const std::pair<const std::string, Json_wrapper> elt2= it2.elt(); const std::string key1= elt1.first; const std::string key2= elt2.first; cmp= compare_json_strings(key1.data(), key1.size(), key2.data(), key2.size()); if (cmp != 0) return cmp; cmp= elt1.second.compare(elt2.second); if (cmp != 0) return cmp; it1.next(); it2.next(); } assert(it1.empty()); assert(it2.empty()); // No differences found. The two objects must be equal. return 0; } case Json_dom::J_STRING: return compare_json_strings(get_data(), get_data_length(), other.get_data(), other.get_data_length()); case Json_dom::J_INT: // Signed integers can be compared to all other numbers. switch (other_type) { case Json_dom::J_INT: return compare_numbers(get_int(), other.get_int()); case Json_dom::J_UINT: return compare_json_int_uint(get_int(), other.get_uint()); case Json_dom::J_DOUBLE: return -compare_json_double_int(other.get_double(), get_int()); case Json_dom::J_DECIMAL: { my_decimal b_dec; if (other.get_decimal_data(&b_dec)) return 1; /* purecov: inspected */ return -compare_json_decimal_int(b_dec, get_int()); } default:; } break; case Json_dom::J_UINT: // Unsigned integers can be compared to all other numbers. switch (other_type) { case Json_dom::J_UINT: return compare_numbers(get_uint(), other.get_uint()); case Json_dom::J_INT: return -compare_json_int_uint(other.get_int(), get_uint()); case Json_dom::J_DOUBLE: return -compare_json_double_uint(other.get_double(), get_uint()); case Json_dom::J_DECIMAL: { my_decimal b_dec; if (other.get_decimal_data(&b_dec)) return 1; /* purecov: inspected */ return -compare_json_decimal_uint(b_dec, get_uint()); } default:; } break; case Json_dom::J_DOUBLE: // Doubles can be compared to all other numbers. { switch (other_type) { case Json_dom::J_DOUBLE: return compare_numbers(get_double(), other.get_double()); case Json_dom::J_INT: return compare_json_double_int(get_double(), other.get_int()); case Json_dom::J_UINT: return compare_json_double_uint(get_double(), other.get_uint()); case Json_dom::J_DECIMAL: { my_decimal other_dec; if (other.get_decimal_data(&other_dec)) return 1; /* purecov: inspected */ return -compare_json_decimal_double(other_dec, get_double()); } default:; } break; } case Json_dom::J_DECIMAL: // Decimals can be compared to all other numbers. { my_decimal a_dec; my_decimal b_dec; if (get_decimal_data(&a_dec)) return 1; /* purecov: inspected */ switch (other_type) { case Json_dom::J_DECIMAL: if (other.get_decimal_data(&b_dec)) return 1; /* purecov: inspected */ /* my_decimal_cmp() treats -0 and 0 as not equal, so check for zero first. */ if (my_decimal_is_zero(&a_dec) && my_decimal_is_zero(&b_dec)) return 0; return my_decimal_cmp(&a_dec, &b_dec); case Json_dom::J_INT: return compare_json_decimal_int(a_dec, other.get_int()); case Json_dom::J_UINT: return compare_json_decimal_uint(a_dec, other.get_uint()); case Json_dom::J_DOUBLE: return compare_json_decimal_double(a_dec, other.get_double()); default:; } break; } case Json_dom::J_BOOLEAN: // Booleans are only equal to other booleans. false is less than true. return compare_numbers(get_boolean(), other.get_boolean()); case Json_dom::J_DATETIME: case Json_dom::J_TIMESTAMP: // Timestamps and datetimes can be equal to each other. { MYSQL_TIME val_a; get_datetime(&val_a); MYSQL_TIME val_b; other.get_datetime(&val_b); return compare_numbers(TIME_to_longlong_packed(&val_a), TIME_to_longlong_packed(&val_b)); } case Json_dom::J_TIME: case Json_dom::J_DATE: // Dates and times can only be equal to values of the same type. { assert(this_type == other_type); MYSQL_TIME val_a; get_datetime(&val_a); MYSQL_TIME val_b; other.get_datetime(&val_b); return compare_numbers(TIME_to_longlong_packed(&val_a), TIME_to_longlong_packed(&val_b)); } case Json_dom::J_OPAQUE: /* Opaque values are equal to other opaque values with the same field type and the same binary representation. */ cmp= compare_numbers(field_type(), other.field_type()); if (cmp == 0) cmp= compare_json_strings(get_data(), get_data_length(), other.get_data(), other.get_data_length()); return cmp; case Json_dom::J_NULL: // Null is always equal to other nulls. assert(this_type == other_type); return 0; case Json_dom::J_ERROR: break; } DBUG_ABORT(); /* purecov: inspected */ return 1; /* purecov: inspected */ } /** Push a warning about a problem encountered when coercing a JSON value to some other data type. @param[in] target_type the name of the target type of the coercion @param[in] error_code the error code to use for the warning @param[in] msgnam the name of the field/expression being coerced */ static void push_json_coercion_warning(const char *target_type, int error_code, const char *msgnam) { /* One argument is no longer used (the empty string), but kept to avoid changing error message format. */ push_warning_printf(current_thd, Sql_condition::SL_WARNING, error_code, ER_THD(current_thd, error_code), target_type, "", msgnam, current_thd->get_stmt_da()->current_row_for_condition()); } longlong Json_wrapper::coerce_int(const char *msgnam) const { switch (type()) { case Json_dom::J_UINT: return static_cast<longlong>(get_uint()); case Json_dom::J_INT: return get_int(); case Json_dom::J_STRING: { /* For a string result, we must first get the string and then convert it to a longlong. */ const char *start= get_data(); size_t length= get_data_length(); char *end= const_cast<char *>(start + length); const CHARSET_INFO *cs= &my_charset_utf8mb4_bin; int error; longlong value= cs->cset->strtoll10(cs, start, &end, &error); if (error > 0 || end != start + length) { int code= (error == MY_ERRNO_ERANGE ? ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE : ER_INVALID_JSON_VALUE_FOR_CAST); push_json_coercion_warning("INTEGER", code, msgnam); } return value; } case Json_dom::J_BOOLEAN: return get_boolean() ? 1 : 0; case Json_dom::J_DECIMAL: { longlong i; my_decimal decimal_value; get_decimal_data(&decimal_value); /* We do not know if this int is destined for signed or unsigned usage, so just get longlong from the value using the sign in the decimal. */ my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, !decimal_value.sign(), &i); return i; } case Json_dom::J_DOUBLE: { // logic here is borrowed from Field_double::val_int double j= get_double(); longlong res; if (j <= (double) LLONG_MIN) { res= LLONG_MIN; } else if (j >= (double) (ulonglong) LLONG_MAX) { res= LLONG_MAX; } else { return (longlong) rint(j); } push_json_coercion_warning("INTEGER", ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE, msgnam); return res; } default:; } push_json_coercion_warning("INTEGER", ER_INVALID_JSON_VALUE_FOR_CAST, msgnam); return 0; } double Json_wrapper::coerce_real(const char *msgnam) const { switch (type()) { case Json_dom::J_DECIMAL: { double dbl; my_decimal decimal_value; get_decimal_data(&decimal_value); my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &dbl); return dbl; } case Json_dom::J_STRING: { /* For a string result, we must first get the string and then convert it to a double. */ const char *start= get_data(); size_t length= get_data_length(); char *end= const_cast<char*>(start) + length; const CHARSET_INFO *cs= &my_charset_utf8mb4_bin; int error; double value= my_strntod(cs, const_cast<char*>(start), length, &end, &error); if (error || end != start + length) { int code= (error == EOVERFLOW ? ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE : ER_INVALID_JSON_VALUE_FOR_CAST); push_json_coercion_warning("DOUBLE", code, msgnam); } return value; } case Json_dom::J_DOUBLE: return get_double(); case Json_dom::J_INT: return static_cast<double>(get_int()); case Json_dom::J_UINT: return static_cast<double>(get_uint()); case Json_dom::J_BOOLEAN: return static_cast<double>(get_boolean()); default:; } push_json_coercion_warning("DOUBLE", ER_INVALID_JSON_VALUE_FOR_CAST, msgnam); return 0.0; } my_decimal *Json_wrapper::coerce_decimal(my_decimal *decimal_value, const char *msgnam) const { switch (type()) { case Json_dom::J_DECIMAL: get_decimal_data(decimal_value); return decimal_value; case Json_dom::J_STRING: { /* For a string result, we must first get the string and then convert it to a decimal. */ // has own error handling, but not very informative int err= str2my_decimal(E_DEC_FATAL_ERROR, get_data(), get_data_length(), &my_charset_utf8mb4_bin, decimal_value); if (err) { int code= (err == E_DEC_OVERFLOW ? ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE : ER_INVALID_JSON_VALUE_FOR_CAST); push_json_coercion_warning("DECIMAL", code, msgnam); } return decimal_value; } case Json_dom::J_DOUBLE: if (double2my_decimal(E_DEC_FATAL_ERROR, get_double(), decimal_value)) { push_json_coercion_warning("DECIMAL", ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE, msgnam); } return decimal_value; case Json_dom::J_INT: if (longlong2decimal(get_int(), decimal_value)) { push_json_coercion_warning("DECIMAL", ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE, msgnam); } return decimal_value; case Json_dom::J_UINT: if (longlong2decimal(get_uint(), decimal_value)) { push_json_coercion_warning("DECIMAL", ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE, msgnam); } return decimal_value; case Json_dom::J_BOOLEAN: // no danger of overflow, so void result (void)int2my_decimal(E_DEC_FATAL_ERROR, get_boolean(), true /* unsigned */, decimal_value); return decimal_value; default:; } push_json_coercion_warning("DECIMAL", ER_INVALID_JSON_VALUE_FOR_CAST, msgnam); my_decimal_set_zero(decimal_value); return decimal_value; } bool Json_wrapper::coerce_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate, const char *msgnam) const { bool result= coerce_time(ltime, msgnam); if (!result && ltime->time_type == MYSQL_TIMESTAMP_TIME) { MYSQL_TIME tmp= *ltime; time_to_datetime(current_thd, &tmp, ltime); } return result; } bool Json_wrapper::coerce_time(MYSQL_TIME *ltime, const char *msgnam) const { switch (type()) { case Json_dom::J_DATETIME: case Json_dom::J_DATE: case Json_dom::J_TIME: case Json_dom::J_TIMESTAMP: set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME); get_datetime(ltime); return false; default: push_json_coercion_warning("DATE/TIME/DATETIME/TIMESTAMP", ER_INVALID_JSON_VALUE_FOR_CAST, msgnam); return true; } } /// Wrapper around a sort key buffer. class Wrapper_sort_key { private: uchar *m_buffer; ///< the buffer into which to write size_t m_length; ///< the length of the buffer size_t m_pos; ///< the current position in the buffer public: Wrapper_sort_key(uchar *buf, size_t len) : m_buffer(buf), m_length(len), m_pos(0) {} /// Get the remaining space in the buffer. size_t remaining() const { return m_length - m_pos; } /// Append a character to the buffer. void append(uchar ch) { if (m_pos < m_length) m_buffer[m_pos++]= ch; } /// Pad the buffer with the specified character. void pad_fill(uchar pad_character, size_t length) { size_t num_chars= std::min(remaining(), length); memset(m_buffer + m_pos, pad_character, num_chars); m_pos += num_chars; } /** Copy an integer to the buffer and format it in a way that makes it possible to sort the integers with memcpy(). @param target_length the number of bytes to write to the buffer @param from the buffer to copy the integer from (in little-endian format) @param from_length the size of the from buffer @param is_unsigned true if the from buffer contains an unsigned integer, false otherwise */ void copy_int(size_t target_length, const uchar* from, size_t from_length, bool is_unsigned) { size_t to_length= std::min(remaining(), target_length); copy_integer<false>(m_buffer + m_pos, to_length, from, from_length, is_unsigned); m_pos+= to_length; } /** Append a string to the buffer, and add the length of the string to the end of the buffer. The space between the end of the string and the beginning of the length field is padded with zeros. */ void append_str_and_len(const char *str, size_t len) { /* The length is written as a four byte value at the end of the buffer, provided that there is enough room. */ size_t space_for_len= std::min(static_cast<size_t>(4), remaining()); /* The string contents are written up to where the length is stored, and get truncated if the string is longer than that. */ size_t space_for_str= remaining() - space_for_len; size_t copy_len= std::min(len, space_for_str); memcpy(m_buffer + m_pos, str, copy_len); m_pos+= copy_len; /* Fill the space between the end of the string and the beginning of the length with zeros. */ pad_fill(0, space_for_str - copy_len); /* Write the length in a format that memcmp() knows how to sort. First we store it in little-endian format in a four-byte buffer, and then we use copy_integer to transform it into a format that works with memcmp(). */ uchar length_buffer[4]; int4store(length_buffer, static_cast<uint32>(len)); copy_int(space_for_len, length_buffer, sizeof(length_buffer), true); // The entire buffer has been filled when we are done here. m_pos= m_length; } }; /// Helper class for building a hash key. class Wrapper_hash_key { private: ulonglong m_crc; public: Wrapper_hash_key(ulonglong *hash_val) : m_crc(*hash_val) {} /** Return the computed hash value. */ ulonglong get_crc() { return m_crc; } void add_character(uchar ch) { add_to_crc(ch); } void add_integer(longlong ll) { char tmp[8]; int8store(tmp, ll); add_string(tmp, sizeof(tmp)); } void add_double(double d) { // Make -0.0 and +0.0 have the same key. if (d == 0) { add_character(0); return; } char tmp[8]; float8store(tmp, d); add_string(tmp, sizeof(tmp)); } void add_string(const char *str, size_t len) { for (size_t idx= 0; idx < len; idx++) { add_to_crc(*str++); } } private: /** Add another character to the evolving crc. @param[in] ch The character to add */ void add_to_crc(uchar ch) { // This logic was cribbed from sql_executor.cc/unique_hash m_crc=((m_crc << 8) + (((uchar) ch))) + (m_crc >> (8*sizeof(ha_checksum)-8)); } }; /// Check if a character represents a non-zero digit. static inline bool is_non_zero_digit(char ch) { return ch >= '1' && ch <= '9'; } /* Type identifiers used in the sort key generated by Json_wrapper::make_sort_key(). Types with lower identifiers sort before types with higher identifiers. */ #define JSON_KEY_NULL '\x00' #define JSON_KEY_NUMBER_NEG '\x01' #define JSON_KEY_NUMBER_ZERO '\x02' #define JSON_KEY_NUMBER_POS '\x03' #define JSON_KEY_STRING '\x04' #define JSON_KEY_OBJECT '\x05' #define JSON_KEY_ARRAY '\x06' #define JSON_KEY_FALSE '\x07' #define JSON_KEY_TRUE '\x08' #define JSON_KEY_DATE '\x09' #define JSON_KEY_TIME '\x0A' #define JSON_KEY_DATETIME '\x0B' #define JSON_KEY_OPAQUE '\x0C' /** Make a sort key for a JSON numeric value from its string representation. The input string could be either on scientific format (such as 1.234e2) or on plain format (such as 12.34). The sort key will have the following parts: 1) One byte that is JSON_KEY_NUMBER_NEG, JSON_KEY_NUMBER_ZERO or JSON_KEY_NUMBER_POS if the number is positive, zero or negative, respectively. 2) Two bytes that represent the decimal exponent of the number (log10 of the number, truncated to an integer). 3) All the digits of the number, without leading zeros. 4) Padding to ensure that equal numbers sort equal even if they have a different number of trailing zeros. If the number is zero, parts 2, 3 and 4 are skipped. For negative numbers, the values in parts 2, 3 and 4 need to be inverted so that bigger negative numbers sort before smaller negative numbers. @param[in] from the string representation of the number @param[in] len the length of the input string @param[in] negative true if the number is negative, false otherwise @param[in,out] to the target sort key */ static void make_json_numeric_sort_key(const char *from, size_t len, bool negative, Wrapper_sort_key *to) { const char *end= from + len; // Find the start of the exponent part, if there is one. const char *end_of_digits= std::find(from, end, 'e'); /* Find the first significant digit. Skip past sign, leading zeros and the decimal point, until the first non-zero digit is found. */ const char *first_significant_digit= std::find_if(from, end_of_digits, is_non_zero_digit); if (first_significant_digit == end_of_digits) { // We didn't find any significant digits, so the number is zero. to->append(JSON_KEY_NUMBER_ZERO); return; } longlong exp; if (end_of_digits != end) { // Scientific format. Fetch the exponent part after the 'e'. char *endp= const_cast<char *>(end); exp= my_strtoll(end_of_digits + 1, &endp, 10); } else { /* Otherwise, find the exponent by calculating the distance between the first significant digit and the decimal point. */ const char *dec_point= std::find(from, end_of_digits, '.'); if (!dec_point) { // There is no decimal point. Just count the digits. exp= end_of_digits - first_significant_digit - 1; } else if (first_significant_digit < dec_point) { // Non-negative exponent. exp= dec_point - first_significant_digit - 1; } else { // Negative exponent. exp= dec_point - first_significant_digit; } } if (negative) { to->append(JSON_KEY_NUMBER_NEG); /* For negative numbers, we have to invert the exponents so that numbers with high exponents sort before numbers with low exponents. */ exp= -exp; } else { to->append(JSON_KEY_NUMBER_POS); } /* Store the exponent part before the digits. Since the decimal exponent of a double can be in the range [-323, +308], we use two bytes for the exponent. (Decimals and bigints also fit in that range.) */ uchar exp_buff[2]; int2store(exp_buff, static_cast<int16>(exp)); to->copy_int(sizeof(exp_buff), exp_buff, sizeof(exp_buff), false); /* Append all the significant digits of the number. Stop before the exponent part if there is one, otherwise go to the end of the string. */ for (const char *ch= first_significant_digit; ch < end_of_digits; ++ch) { if (my_isdigit(&my_charset_numeric, *ch)) { /* If the number is negative, the digits must be inverted so that big negative numbers sort before small negative numbers. */ if (negative) to->append('9' - *ch + '0'); else to->append(*ch); } } /* Pad the rest of the buffer with zeros, so that the number of trailing zeros doesn't affect how the number is sorted. As above, we need to invert the digits for negative numbers. */ to->pad_fill(negative ? '9' : '0', to->remaining()); } void Json_wrapper::make_sort_key(uchar *to, size_t to_length) const { Wrapper_sort_key key(to, to_length); const Json_dom::enum_json_type jtype= type(); switch (jtype) { case Json_dom::J_NULL: key.append(JSON_KEY_NULL); break; case Json_dom::J_DECIMAL: { my_decimal dec; if (get_decimal_data(&dec)) break; /* purecov: inspected */ char buff[DECIMAL_MAX_STR_LENGTH + 1]; String str(buff, sizeof(buff), &my_charset_numeric); if (my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str)) break; /* purecov: inspected */ make_json_numeric_sort_key(str.ptr(), str.length(), dec.sign(), &key); break; } case Json_dom::J_INT: { longlong i= get_int(); char buff[MAX_BIGINT_WIDTH + 1]; size_t len= longlong10_to_str(i, buff, -10) - buff; make_json_numeric_sort_key(buff, len, i < 0, &key); break; } case Json_dom::J_UINT: { ulonglong ui= get_uint(); char buff[MAX_BIGINT_WIDTH + 1]; size_t len= longlong10_to_str(ui, buff, 10) - buff; make_json_numeric_sort_key(buff, len, false, &key); break; } case Json_dom::J_DOUBLE: { double dbl= get_double(); char buff[MY_GCVT_MAX_FIELD_WIDTH + 1]; size_t len= my_gcvt(dbl, MY_GCVT_ARG_DOUBLE, sizeof(buff) - 1, buff, NULL); make_json_numeric_sort_key(buff, len, (dbl < 0), &key); break; } case Json_dom::J_STRING: key.append(JSON_KEY_STRING); key.append_str_and_len(get_data(), get_data_length()); break; case Json_dom::J_OBJECT: case Json_dom::J_ARRAY: /* Internal ordering of objects and arrays only considers length for now. */ { key.append(jtype == Json_dom::J_OBJECT ? JSON_KEY_OBJECT : JSON_KEY_ARRAY); uchar len[4]; int4store(len, static_cast<uint32>(length())); key.copy_int(sizeof(len), len, sizeof(len), true); /* Raise a warning to give an indication that sorting of objects and arrays is not properly supported yet. The warning is raised for each object/array that is found during the sort, but Filesort_error_handler will make sure that only one warning is seen on the top level for every sort. */ push_warning_printf(current_thd, Sql_condition::SL_WARNING, ER_NOT_SUPPORTED_YET, ER_THD(current_thd, ER_NOT_SUPPORTED_YET), "sorting of non-scalar JSON values"); break; } case Json_dom::J_BOOLEAN: key.append(get_boolean() ? JSON_KEY_TRUE : JSON_KEY_FALSE); break; case Json_dom::J_DATE: case Json_dom::J_TIME: case Json_dom::J_DATETIME: case Json_dom::J_TIMESTAMP: { if (jtype == Json_dom::J_DATE) key.append(JSON_KEY_DATE); else if (jtype == Json_dom::J_TIME) key.append(JSON_KEY_TIME); else key.append(JSON_KEY_DATETIME); /* Temporal values are stored in the packed format in the binary JSON format. The packed values are 64-bit signed little-endian integers. */ const size_t packed_length= Json_datetime::PACKED_SIZE; char tmp[packed_length]; const char *packed= get_datetime_packed(tmp); key.copy_int(packed_length, pointer_cast<const uchar *>(packed), packed_length, false); break; } case Json_dom::J_OPAQUE: key.append(JSON_KEY_OPAQUE); key.append(field_type()); key.append_str_and_len(get_data(), get_data_length()); break; case Json_dom::J_ERROR: break; } key.pad_fill(0, key.remaining()); } ulonglong Json_wrapper::make_hash_key(ulonglong *hash_val) { Wrapper_hash_key hash_key(hash_val); const Json_dom::enum_json_type jtype= type(); switch (jtype) { case Json_dom::J_NULL: hash_key.add_character(JSON_KEY_NULL); break; case Json_dom::J_DECIMAL: { my_decimal dec; if (get_decimal_data(&dec)) break; /* purecov: inspected */ double dbl; decimal2double(&dec, &dbl); hash_key.add_double(dbl); break; } case Json_dom::J_INT: hash_key.add_double(static_cast<double>(get_int())); break; case Json_dom::J_UINT: hash_key.add_double(ulonglong2double(get_uint())); break; case Json_dom::J_DOUBLE: hash_key.add_double(get_double()); break; case Json_dom::J_STRING: case Json_dom::J_OPAQUE: hash_key.add_string(get_data(), get_data_length()); break; case Json_dom::J_OBJECT: { hash_key.add_character(JSON_KEY_OBJECT); for (Json_wrapper_object_iterator it(object_iterator()); !it.empty(); it.next()) { std::pair<const std::string, Json_wrapper> pair= it.elt(); hash_key.add_string(pair.first.c_str(), pair.first.length()); ulonglong t= hash_key.get_crc(); hash_key.add_integer(pair.second.make_hash_key(&t)); } break; } case Json_dom::J_ARRAY: { hash_key.add_character(JSON_KEY_ARRAY); size_t elts= length(); for (uint i= 0; i < elts; i++) { ulonglong t= hash_key.get_crc(); hash_key.add_integer((*this)[i].make_hash_key(&t)); } break; } case Json_dom::J_BOOLEAN: hash_key.add_character(get_boolean() ? JSON_KEY_TRUE : JSON_KEY_FALSE); break; case Json_dom::J_DATE: case Json_dom::J_TIME: case Json_dom::J_DATETIME: case Json_dom::J_TIMESTAMP: { const size_t packed_length= Json_datetime::PACKED_SIZE; char tmp[packed_length]; const char *packed= get_datetime_packed(tmp); hash_key.add_string(packed, packed_length); break; } case Json_dom::J_ERROR: DBUG_ABORT(); /* purecov: inspected */ break; /* purecov: inspected */ } ulonglong result= hash_key.get_crc(); return result; }