#include #include "functor_json.h" #include "functioncolumn.h" #include "rowgroup.h" #include "treenode.h" using namespace execplan; using namespace rowgroup; #include "dataconvert.h" #include "jsonhelpers.h" using namespace funcexp::helpers; namespace funcexp { int Func_json_extract::doExtract(Row& row, FunctionParm& fp, json_value_types* type, string& retJS, bool compareWhole = true) { bool isNull = false; const auto js = fp[0]->data()->getStrVal(row, isNull); if (isNull) return 1; const char* rawJS = js.str(); json_engine_t jsEg, savJSEg; json_path_t p; const uchar* value; bool notFirstVal = false; size_t valLen; bool mayMulVal; int wildcards; bool isMatch; #ifdef MYSQL_GE_1009 int arrayCounter[JSON_DEPTH_LIMIT]; bool hasNegPath = false; #endif const size_t argSize = fp.size(); string tmp; initJSPaths(paths, fp, 1, 1); for (size_t i = 1; i < argSize; i++) { JSONPath& path = paths[i - 1]; path.p.types_used = JSON_PATH_KEY_NULL; if (!path.parsed && parseJSPath(path, row, fp[i])) return 1; #ifdef MYSQL_GE_1009 hasNegPath |= path.p.types_used & JSON_PATH_NEGATIVE_INDEX; #endif } #ifdef MYSQL_GE_1009 wildcards = (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD | JSON_PATH_ARRAY_RANGE); #else wildcards = (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD); #endif mayMulVal = argSize > 2 || (paths[0].p.types_used & wildcards); *type = mayMulVal ? JSON_VALUE_ARRAY : JSON_VALUE_NULL; if (compareWhole) { retJS.clear(); if (mayMulVal) retJS.append("["); } json_get_path_start(&jsEg, getCharset(fp[0]), (const uchar*)rawJS, (const uchar*)rawJS + js.length(), &p); while (json_get_path_next(&jsEg, &p) == 0) { #ifdef MYSQL_GE_1009 if (hasNegPath && jsEg.value_type == JSON_VALUE_ARRAY && json_skip_array_and_count(&jsEg, arrayCounter + (p.last_step - p.steps))) return 1; #endif #ifdef MYSQL_GE_1009 isMatch = matchJSPath(paths, &p, jsEg.value_type, arrayCounter, false); #else isMatch = matchJSPath(paths, &p, jsEg.value_type, nullptr, false); #endif if (!isMatch) continue; value = jsEg.value_begin; if (*type == JSON_VALUE_NULL) *type = jsEg.value_type; /* we only care about the first found value */ if (!compareWhole) { retJS = js.safeString(""); return 0; } if (json_value_scalar(&jsEg)) valLen = jsEg.value_end - value; else { if (mayMulVal) savJSEg = jsEg; if (json_skip_level(&jsEg)) return 1; valLen = jsEg.s.c_str - value; if (mayMulVal) jsEg = savJSEg; } if (notFirstVal) retJS.append(", "); retJS.append((const char*)value, valLen); notFirstVal = true; if (!mayMulVal) { /* Loop to the end of the JSON just to make sure it's valid. */ while (json_get_path_next(&jsEg, &p) == 0) { } break; } } if (unlikely(jsEg.s.error)) return 1; if (!notFirstVal) /* Nothing was found. */ return 1; if (mayMulVal) retJS.append("]"); utils::NullString retJS_ns(retJS); initJSEngine(jsEg, getCharset(fp[0]), retJS_ns); if (doFormat(&jsEg, tmp, Func_json_format::LOOSE)) return 1; retJS.clear(); retJS.swap(tmp); return 0; } CalpontSystemCatalog::ColType Func_json_extract::operationType(FunctionParm& fp, CalpontSystemCatalog::ColType& resultType) { return fp[0]->data()->resultType(); } string Func_json_extract::getStrVal(Row& row, FunctionParm& fp, bool& isNull, CalpontSystemCatalog::ColType& type) { string retJS; json_value_types valType; if (doExtract(row, fp, &valType, retJS) == 0) return retJS; isNull = true; return ""; } int64_t Func_json_extract::getIntVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, execplan::CalpontSystemCatalog::ColType& type) { string retJS; json_value_types valType; int64_t ret = 0; if (doExtract(row, fp, &valType, retJS, false) == 0) { switch (valType) { case JSON_VALUE_NUMBER: case JSON_VALUE_STRING: { char* end; int err; ret = getCharset(fp[0])->strntoll(retJS.data(), retJS.size(), 10, &end, &err); break; } case JSON_VALUE_TRUE: ret = 1; break; default: break; }; } return ret; } double Func_json_extract::getDoubleVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, execplan::CalpontSystemCatalog::ColType& type) { string retJS; json_value_types valType; double ret = 0.0; if (doExtract(row, fp, &valType, retJS, false) == 0) { switch (valType) { case JSON_VALUE_NUMBER: case JSON_VALUE_STRING: { char* end; int err; ret = getCharset(fp[0])->strntod(retJS.data(), retJS.size(), &end, &err); break; } case JSON_VALUE_TRUE: ret = 1.0; break; default: break; }; } return ret; } execplan::IDB_Decimal Func_json_extract::getDecimalVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, execplan::CalpontSystemCatalog::ColType& type) { json_value_types valType; string retJS; if (doExtract(row, fp, &valType, retJS, false) == 0) { switch (valType) { case JSON_VALUE_STRING: case JSON_VALUE_NUMBER: return fp[0]->data()->getDecimalVal(row, isNull); case JSON_VALUE_TRUE: return IDB_Decimal(1, 0, 1); case JSON_VALUE_OBJECT: case JSON_VALUE_ARRAY: case JSON_VALUE_FALSE: case JSON_VALUE_NULL: case JSON_VALUE_UNINITIALIZED: break; }; } return IDB_Decimal(0, 0, 1); } } // namespace funcexp