You've already forked mariadb-columnstore-engine
							
							
				mirror of
				https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
				synced 2025-10-31 18:30:33 +03:00 
			
		
		
		
	This patch is the columnstore-part of the task. Columnstore wanted to have previous 32 depth, so this patch aims at keeping the compatibility.
		
			
				
	
	
		
			388 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "jsonhelpers.h"
 | |
| using namespace std;
 | |
| 
 | |
| namespace funcexp
 | |
| {
 | |
| namespace helpers
 | |
| {
 | |
| int setupJSPath(json_path_t* path, CHARSET_INFO* cs, const utils::NullString& str, bool wildcards = true)
 | |
| {
 | |
|   int err = json_path_setup(path, cs, (const uchar*)str.str(), (const uchar*)str.end());
 | |
|   if (wildcards)
 | |
|     return err;
 | |
| 
 | |
|   if (!err)
 | |
|   {
 | |
| #if MYSQL_VERSION_ID >= 100900
 | |
|     bool support = (path->types_used & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD | JSON_PATH_ARRAY_RANGE)) == 0;
 | |
| #else
 | |
|     bool support = (path->types_used & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD)) == 0;
 | |
| #endif
 | |
|     if (support)
 | |
|       return 0;
 | |
|     path->s.error = NO_WILDCARD_ALLOWED;
 | |
|   }
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| bool appendEscapedJS(string& ret, const CHARSET_INFO* retCS, const utils::NullString& js,
 | |
|                      const CHARSET_INFO* jsCS)
 | |
| {
 | |
|   const int jsLen = js.length();
 | |
|   const char* rawJS = js.str();
 | |
|   int strLen = jsLen * 12 * jsCS->mbmaxlen / jsCS->mbminlen;
 | |
|   char* buf = (char*)alloca(strLen + 1);
 | |
|   if ((strLen = json_escape(retCS, (const uchar*)rawJS, (const uchar*)rawJS + jsLen, jsCS, (uchar*)buf,
 | |
|                             (uchar*)buf + strLen)) >= 0)
 | |
|   {
 | |
|     buf[strLen] = '\0';
 | |
|     ret.append(buf, strLen);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool appendJSKeyName(string& ret, const CHARSET_INFO* retCS, rowgroup::Row& row, execplan::SPTP& parm)
 | |
| {
 | |
|   bool nullVal = false;
 | |
|   const auto& js = parm->data()->getStrVal(row, nullVal);
 | |
|   if (nullVal)
 | |
|   {
 | |
|     ret.append("\"\": ");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   ret.append("\"");
 | |
|   if (appendEscapedJS(ret, retCS, js, parm->data()->resultType().getCharset()))
 | |
|     return true;
 | |
|   ret.append("\": ");
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool appendJSValue(string& ret, const CHARSET_INFO* retCS, rowgroup::Row& row, execplan::SPTP& parm)
 | |
| {
 | |
|   bool nullVal = false;
 | |
|   const auto& js = parm->data()->getStrVal(row, nullVal);
 | |
|   if (nullVal)
 | |
|   {
 | |
|     ret.append("null");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   datatypes::SystemCatalog::ColDataType dataType = parm->data()->resultType().colDataType;
 | |
|   if (dataType == datatypes::SystemCatalog::BIGINT && (js == "true" || js == "false"))
 | |
|   {
 | |
|     ret.append(js.safeString(""));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   const CHARSET_INFO* jsCS = parm->data()->resultType().getCharset();
 | |
|   if (isCharType(dataType))
 | |
|   {
 | |
|     ret.append("\"");
 | |
|     if (appendEscapedJS(ret, retCS, js, jsCS))
 | |
|       return true;
 | |
|     ret.append("\"");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return appendEscapedJS(ret, retCS, js, jsCS);
 | |
| }
 | |
| 
 | |
| int appendTab(string& js, const int depth, const int tabSize)
 | |
| {
 | |
|   try
 | |
|   {
 | |
|     js.append("\n");
 | |
|     for (int i = 0; i < depth; i++)
 | |
|     {
 | |
|       js.append(tab_arr, tabSize);
 | |
|     }
 | |
|   }
 | |
|   catch (const std::exception& e)
 | |
|   {
 | |
|     return 1;
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int doFormat(json_engine_t* je, string& niceJS, Func_json_format::FORMATS mode, int tabSize)
 | |
| {
 | |
|   int depth = 0;
 | |
|   static const char *comma = ", ", *colon = "\": ";
 | |
|   uint commaLen, colonLen;
 | |
|   int firstValue = 1;
 | |
| 
 | |
|   niceJS.reserve(je->s.str_end - je->s.c_str + 32);
 | |
| 
 | |
|   assert(mode != Func_json_format::DETAILED || (tabSize >= 0 && tabSize <= TAB_SIZE_LIMIT));
 | |
| 
 | |
|   if (mode == Func_json_format::LOOSE)
 | |
|   {
 | |
|     commaLen = 2;
 | |
|     colonLen = 3;
 | |
|   }
 | |
|   else if (mode == Func_json_format::DETAILED)
 | |
|   {
 | |
|     commaLen = 1;
 | |
|     colonLen = 3;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     commaLen = 1;
 | |
|     colonLen = 2;
 | |
|   }
 | |
| 
 | |
|   do
 | |
|   {
 | |
|     switch (je->state)
 | |
|     {
 | |
|       case JST_KEY:
 | |
|       {
 | |
|         const uchar* key_start = je->s.c_str;
 | |
|         const uchar* key_end;
 | |
| 
 | |
|         do
 | |
|         {
 | |
|           key_end = je->s.c_str;
 | |
|         } while (json_read_keyname_chr(je) == 0);
 | |
| 
 | |
|         if (unlikely(je->s.error))
 | |
|           goto error;
 | |
| 
 | |
|         if (!firstValue)
 | |
|           niceJS.append(comma, commaLen);
 | |
| 
 | |
|         if (mode == Func_json_format::DETAILED && appendTab(niceJS, depth, tabSize))
 | |
|           goto error;
 | |
| 
 | |
|         niceJS.append("\"");
 | |
|         niceJS.append((const char*)key_start, (int)(key_end - key_start));
 | |
|         niceJS.append(colon, colonLen);
 | |
|       }
 | |
|         /* now we have key value to handle, so no 'break'. */
 | |
|         DBUG_ASSERT(je->state == JST_VALUE);
 | |
|         goto handle_value;
 | |
| 
 | |
|       case JST_VALUE:
 | |
|         if (!firstValue)
 | |
|           niceJS.append(comma, commaLen);
 | |
| 
 | |
|         if (mode == Func_json_format::DETAILED && depth > 0 && appendTab(niceJS, depth, tabSize))
 | |
|           goto error;
 | |
| 
 | |
|       handle_value:
 | |
|         if (json_read_value(je))
 | |
|           goto error;
 | |
|         if (json_value_scalar(je))
 | |
|         {
 | |
|           niceJS.append((const char*)je->value_begin, (int)(je->value_end - je->value_begin));
 | |
| 
 | |
|           firstValue = 0;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|           if (mode == Func_json_format::DETAILED && depth > 0 && appendTab(niceJS, depth, tabSize))
 | |
|             goto error;
 | |
|           niceJS.append((je->value_type == JSON_VALUE_OBJECT) ? "{" : "[");
 | |
|           firstValue = 1;
 | |
|           depth++;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|       case JST_OBJ_END:
 | |
|       case JST_ARRAY_END:
 | |
|         depth--;
 | |
|         if (mode == Func_json_format::DETAILED && appendTab(niceJS, depth, tabSize))
 | |
|           goto error;
 | |
|         niceJS.append((je->state == JST_OBJ_END) ? "}" : "]");
 | |
|         firstValue = 0;
 | |
|         break;
 | |
| 
 | |
|       default: break;
 | |
|     };
 | |
|   } while (json_scan_next(je) == 0);
 | |
| 
 | |
|   return je->s.error || *je->killed_ptr;
 | |
| 
 | |
| error:
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| bool findKeyInObject(json_engine_t* jsEg, json_string_t* key)
 | |
| {
 | |
|   const uchar* str = key->c_str;
 | |
| 
 | |
|   while (json_scan_next(jsEg) == 0 && jsEg->state != JST_OBJ_END)
 | |
|   {
 | |
|     DBUG_ASSERT(jsEg->state == JST_KEY);
 | |
|     if (json_key_matches(jsEg, key))
 | |
|       return true;
 | |
|     if (json_skip_key(jsEg))
 | |
|       return false;
 | |
|     key->c_str = str;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| int cmpPartJSPath(const json_path_step_t* a, const json_path_step_t* aEnd, const json_path_step_t* b,
 | |
|                   const json_path_step_t* bEnd, enum json_value_types vt, const int* arraySize)
 | |
| {
 | |
|   int ret, ret2;
 | |
|   const json_path_step_t* tmpB = b;
 | |
| 
 | |
|   while (a <= aEnd)
 | |
|   {
 | |
|     if (b > bEnd)
 | |
|     {
 | |
|       while (vt != JSON_VALUE_ARRAY && (a->type & JSON_PATH_ARRAY_WILD) == JSON_PATH_ARRAY && a->n_item == 0)
 | |
|       {
 | |
|         if (++a > aEnd)
 | |
|           return 0;
 | |
|       }
 | |
|       return -2;
 | |
|     }
 | |
| 
 | |
|     DBUG_ASSERT((b->type & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD)) == 0);
 | |
| 
 | |
|     if (a->type & JSON_PATH_ARRAY)
 | |
|     {
 | |
|       if (b->type & JSON_PATH_ARRAY)
 | |
|       {
 | |
| #if MYSQL_VERSION_ID >= 100900
 | |
|         int ret = 0, corrected_n_item_a = 0;
 | |
|         if (arraySize)
 | |
|           corrected_n_item_a = a->n_item < 0 ? arraySize[b - tmpB] + a->n_item : a->n_item;
 | |
|         if (a->type & JSON_PATH_ARRAY_RANGE)
 | |
|         {
 | |
|           int corrected_n_item_end_a = 0;
 | |
|           if (arraySize)
 | |
|             corrected_n_item_end_a = a->n_item_end < 0 ? arraySize[b - tmpB] + a->n_item_end : a->n_item_end;
 | |
|           ret = b->n_item >= corrected_n_item_a && b->n_item <= corrected_n_item_end_a;
 | |
|         }
 | |
|         else
 | |
|           ret = corrected_n_item_a == b->n_item;
 | |
| 
 | |
|         if ((a->type & JSON_PATH_WILD) || ret)
 | |
|           goto step_fits;
 | |
|         goto step_failed;
 | |
| #else
 | |
|         if ((a->type & JSON_PATH_WILD) || a->n_item == b->n_item)
 | |
|           goto step_fits;
 | |
|         goto step_failed;
 | |
| #endif
 | |
|       }
 | |
|       if ((a->type & JSON_PATH_WILD) == 0 && a->n_item == 0)
 | |
|         goto step_fits_autowrap;
 | |
|       goto step_failed;
 | |
|     }
 | |
|     else /* JSON_PATH_KEY */
 | |
|     {
 | |
|       if (!(b->type & JSON_PATH_KEY))
 | |
|         goto step_failed;
 | |
| 
 | |
|       if (!(a->type & JSON_PATH_WILD) &&
 | |
|           (a->key_end - a->key != b->key_end - b->key || memcmp(a->key, b->key, a->key_end - a->key) != 0))
 | |
|         goto step_failed;
 | |
| 
 | |
|       goto step_fits;
 | |
|     }
 | |
|   step_failed:
 | |
|     if (!(a->type & JSON_PATH_DOUBLE_WILD))
 | |
|       return -1;
 | |
|     b++;
 | |
|     continue;
 | |
| 
 | |
|   step_fits:
 | |
|     b++;
 | |
|     if (!(a->type & JSON_PATH_DOUBLE_WILD))
 | |
|     {
 | |
|       a++;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     /* Double wild handling needs recursions. */
 | |
|     ret = cmpPartJSPath(a + 1, aEnd, b, bEnd, vt, arraySize ? arraySize + (b - tmpB) : NULL);
 | |
|     if (ret == 0)
 | |
|       return 0;
 | |
| 
 | |
|     ret2 = cmpPartJSPath(a, aEnd, b, bEnd, vt, arraySize ? arraySize + (b - tmpB) : NULL);
 | |
| 
 | |
|     return (ret2 >= 0) ? ret2 : ret;
 | |
| 
 | |
|   step_fits_autowrap:
 | |
|     if (!(a->type & JSON_PATH_DOUBLE_WILD))
 | |
|     {
 | |
|       a++;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     /* Double wild handling needs recursions. */
 | |
|     ret = cmpPartJSPath(a + 1, aEnd, b + 1, bEnd, vt, arraySize ? arraySize + (b - tmpB) : NULL);
 | |
|     if (ret == 0)
 | |
|       return 0;
 | |
| 
 | |
|     ret2 = cmpPartJSPath(a, aEnd, b + 1, bEnd, vt, arraySize ? arraySize + (b - tmpB) : NULL);
 | |
| 
 | |
|     return (ret2 >= 0) ? ret2 : ret;
 | |
|   }
 | |
| 
 | |
|   return b <= bEnd;
 | |
| }
 | |
| 
 | |
| int cmpJSPath(const json_path_t* a, const json_path_t* b, enum json_value_types vt, const int* arraySize)
 | |
| {
 | |
| #if MYSQL_VERSION_ID >= 120200
 | |
|   json_path_step_t *a_last_step= reinterpret_cast<json_path_step_t*>
 | |
|                                    (mem_root_dynamic_array_get_val((MEM_ROOT_DYNAMIC_ARRAY*)&a->steps, (size_t)a->last_step_idx));
 | |
|   json_path_step_t *b_last_step= reinterpret_cast<json_path_step_t*>
 | |
|                                    (mem_root_dynamic_array_get_val((MEM_ROOT_DYNAMIC_ARRAY*)&b->steps, (size_t)b->last_step_idx));
 | |
|   return cmpPartJSPath((reinterpret_cast<json_path_step_t*>(a->steps.buffer)) + 1, a_last_step,
 | |
|                        (reinterpret_cast<json_path_step_t*>(b->steps.buffer)) + 1, b_last_step,
 | |
|                        vt, arraySize);
 | |
| #else
 | |
|   return cmpPartJSPath(a->steps + 1, a->last_step, b->steps + 1, b->last_step, vt, arraySize);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| int parseJSPath(JSONPath& path, rowgroup::Row& row, execplan::SPTP& parm, bool wildcards)
 | |
| {
 | |
|   // check if path column is const
 | |
|   if (!path.constant)
 | |
|     markConstFlag(path, parm);
 | |
| 
 | |
|   bool isNull = false;
 | |
|   const auto& jsp = parm->data()->getStrVal(row, isNull);
 | |
| 
 | |
|   if (isNull || setupJSPath(&path.p, getCharset(parm), jsp, wildcards))
 | |
|     return 1;
 | |
| 
 | |
|   path.parsed = path.constant;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| bool matchJSPath(const std::vector<funcexp::JSONPath>& paths, const json_path_t* p, json_value_types valType,
 | |
|                  [[maybe_unused]] const int* arrayCounter, bool exact)
 | |
| {
 | |
|   for (size_t curr = 0; curr < paths.size(); curr++)
 | |
|   {
 | |
| #if MYSQL_VERSION_ID >= 100900
 | |
|     int cmp = cmpJSPath(&paths[curr].p, p, valType, arrayCounter);
 | |
| #else
 | |
|     int cmp = cmpJSPath(&paths[curr].p, p, valType);
 | |
| #endif
 | |
|     bool ret = exact ? cmp >= 0 : cmp == 0;
 | |
|     if (ret)
 | |
|       return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| }  // namespace helpers
 | |
| }  // namespace funcexp
 |