sql_value.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #pragma once
  2. #include "resource.h"
  3. #include "compile_path.h"
  4. #include <yql/essentials/public/udf/udf_type_builder.h>
  5. #include <yql/essentials/public/udf/udf_value.h>
  6. #include <yql/essentials/public/udf/udf_helpers.h>
  7. #include <yql/essentials/minikql/dom/node.h>
  8. #include <yql/essentials/types/binary_json/read.h>
  9. #include <util/generic/yexception.h>
  10. #include <util/generic/ylimits.h>
  11. #include <util/string/cast.h>
  12. namespace NJson2Udf {
  13. using namespace NKikimr;
  14. using namespace NUdf;
  15. using namespace NYql;
  16. using namespace NDom;
  17. using namespace NJsonPath;
  18. namespace {
  19. template <class TValueType, bool ForceConvert = false>
  20. TUnboxedValue TryConvertJson(const IValueBuilder* valueBuilder, const TUnboxedValue& source) {
  21. Y_UNUSED(valueBuilder);
  22. Y_UNUSED(source);
  23. Y_ABORT("Unsupported type");
  24. }
  25. template <>
  26. TUnboxedValue TryConvertJson<TUtf8>(const IValueBuilder* valueBuilder, const TUnboxedValue& source) {
  27. Y_UNUSED(valueBuilder);
  28. if (IsNodeType(source, ENodeType::String)) {
  29. return source;
  30. }
  31. return {};
  32. }
  33. template <>
  34. TUnboxedValue TryConvertJson<TUtf8, true>(const IValueBuilder* valueBuilder, const TUnboxedValue& source) {
  35. switch (GetNodeType(source)) {
  36. case ENodeType::String:
  37. return source;
  38. case ENodeType::Uint64:
  39. return valueBuilder->NewString(ToString(source.Get<ui64>())).Release();
  40. case ENodeType::Int64:
  41. return valueBuilder->NewString(ToString(source.Get<i64>())).Release();
  42. case ENodeType::Bool:
  43. return source.Get<bool>() ? TUnboxedValuePod::Embedded("true") : TUnboxedValuePod::Embedded("false");
  44. case ENodeType::Double:
  45. return valueBuilder->NewString(ToString(source.Get<double>())).Release();
  46. case ENodeType::Entity:
  47. return TUnboxedValuePod::Embedded("null");
  48. case ENodeType::List:
  49. case ENodeType::Dict:
  50. case ENodeType::Attr:
  51. return {};
  52. }
  53. }
  54. template <>
  55. TUnboxedValue TryConvertJson<i64>(const IValueBuilder* valueBuilder, const TUnboxedValue& source) {
  56. Y_UNUSED(valueBuilder);
  57. if (!source.IsEmbedded()) {
  58. return {};
  59. }
  60. if (IsNodeType(source, ENodeType::Int64)) {
  61. return TUnboxedValuePod(source.Get<i64>());
  62. } else if (IsNodeType(source, ENodeType::Uint64) && source.Get<ui64>() < Max<i64>()) {
  63. return TUnboxedValuePod(static_cast<i64>(source.Get<ui64>()));
  64. } else if (IsNodeType(source, ENodeType::Double) && static_cast<i64>(source.Get<double>()) == source.Get<double>()) {
  65. return TUnboxedValuePod(static_cast<i64>(source.Get<double>()));
  66. }
  67. return {};
  68. }
  69. template <>
  70. TUnboxedValue TryConvertJson<double>(const IValueBuilder* valueBuilder, const TUnboxedValue& source) {
  71. Y_UNUSED(valueBuilder);
  72. if (!source.IsEmbedded()) {
  73. return {};
  74. }
  75. if (IsNodeType(source, ENodeType::Double)) {
  76. return TUnboxedValuePod(source.Get<double>());
  77. } else if (IsNodeType(source, ENodeType::Int64)) {
  78. return TUnboxedValuePod(static_cast<double>(source.Get<i64>()));
  79. } else if (IsNodeType(source, ENodeType::Uint64)) {
  80. return TUnboxedValuePod(static_cast<double>(source.Get<ui64>()));
  81. }
  82. return {};
  83. }
  84. template <>
  85. TUnboxedValue TryConvertJson<bool>(const IValueBuilder* valueBuilder, const TUnboxedValue& source) {
  86. Y_UNUSED(valueBuilder);
  87. if (!source.IsEmbedded() || !IsNodeType(source, ENodeType::Bool)) {
  88. return {};
  89. }
  90. return {TUnboxedValuePod(source.Get<bool>())};
  91. }
  92. }
  93. template <EDataSlot InputType, class TValueType, bool ForceConvert = false>
  94. class TSqlValue: public TBoxedValue {
  95. public:
  96. enum class TErrorCode : ui8 {
  97. Empty = 0,
  98. Error = 1
  99. };
  100. TSqlValue(TSourcePosition pos)
  101. : Pos_(pos)
  102. {
  103. }
  104. static TStringRef Name();
  105. static bool DeclareSignature(
  106. const TStringRef& name,
  107. TType* userType,
  108. IFunctionTypeInfoBuilder& builder,
  109. bool typesOnly) {
  110. Y_UNUSED(userType);
  111. if (name != Name()) {
  112. return false;
  113. }
  114. auto optionalValueType = builder.Optional()->Item<TValueType>().Build();
  115. auto errorTupleType = builder.Tuple(2)->Add<ui8>().Add<char*>().Build();
  116. auto returnTypeTuple = builder.Tuple(2)
  117. ->Add(errorTupleType)
  118. .Add(optionalValueType)
  119. .Build();
  120. auto returnType = builder.Variant()->Over(returnTypeTuple).Build();
  121. TType* jsonType = nullptr;
  122. if constexpr (InputType == EDataSlot::Json) {
  123. jsonType = builder.Resource(JSON_NODE_RESOURCE_NAME);
  124. } else {
  125. jsonType = builder.SimpleType<TJsonDocument>();
  126. }
  127. auto optionalJsonType = builder.Optional()->Item(jsonType).Build();
  128. auto jsonPathType = builder.Resource(JSONPATH_RESOURCE_NAME);
  129. auto dictType = builder.Dict()->Key<TUtf8>().Value(builder.Resource(JSON_NODE_RESOURCE_NAME)).Build();
  130. builder.Args()
  131. ->Add(optionalJsonType)
  132. .Add(jsonPathType)
  133. .Add(dictType)
  134. .Done()
  135. .Returns(returnType);
  136. builder.IsStrict();
  137. if (!typesOnly) {
  138. builder.Implementation(new TSqlValue(builder.GetSourcePosition()));
  139. }
  140. return true;
  141. }
  142. private:
  143. TUnboxedValue BuildErrorResult(const IValueBuilder* valueBuilder, TErrorCode code, const TStringBuf message) const {
  144. TUnboxedValue* items = nullptr;
  145. auto errorTuple = valueBuilder->NewArray(2, items);
  146. items[0] = TUnboxedValuePod(static_cast<ui8>(code));
  147. items[1] = valueBuilder->NewString(message);
  148. return valueBuilder->NewVariant(0, std::move(errorTuple));
  149. }
  150. TUnboxedValue BuildSuccessfulResult(const IValueBuilder* valueBuilder, TUnboxedValue&& value) const {
  151. return valueBuilder->NewVariant(1, std::move(value));
  152. }
  153. TUnboxedValue Run(
  154. const IValueBuilder* valueBuilder,
  155. const TUnboxedValuePod* args) const final {
  156. try {
  157. if (!args[0].HasValue()) {
  158. return BuildSuccessfulResult(valueBuilder, TUnboxedValuePod());
  159. }
  160. TValue jsonDom;
  161. if constexpr (InputType == EDataSlot::JsonDocument) {
  162. jsonDom = TValue(NBinaryJson::TBinaryJsonReader::Make(args[0].AsStringRef())->GetRootCursor());
  163. } else {
  164. jsonDom = TValue(args[0]);
  165. }
  166. auto* jsonPathResource = static_cast<TJsonPathResource*>(args[1].AsBoxed().Get());
  167. const auto& jsonPath = *jsonPathResource->Get();
  168. const auto variables = DictToVariables(args[2]);
  169. const auto result = ExecuteJsonPath(jsonPath, jsonDom, variables, valueBuilder);
  170. if (result.IsError()) {
  171. return BuildErrorResult(valueBuilder, TErrorCode::Error, TStringBuilder() << "Error executing jsonpath:" << Endl << result.GetError() << Endl);
  172. }
  173. const auto& nodes = result.GetNodes();
  174. if (nodes.empty()) {
  175. return BuildErrorResult(valueBuilder, TErrorCode::Empty, "Result is empty");
  176. }
  177. if (nodes.size() > 1) {
  178. return BuildErrorResult(valueBuilder, TErrorCode::Error, "Result consists of multiple items");
  179. }
  180. const auto& value = nodes[0];
  181. if (value.Is(EValueType::Array) || value.Is(EValueType::Object)) {
  182. // SqlValue can return only scalar values
  183. return BuildErrorResult(valueBuilder, TErrorCode::Error, "Extracted JSON value is either object or array");
  184. }
  185. if (value.Is(EValueType::Null)) {
  186. // JSON nulls must be converted to SQL nulls
  187. return BuildSuccessfulResult(valueBuilder, TUnboxedValuePod());
  188. }
  189. const auto source = value.ConvertToUnboxedValue(valueBuilder);
  190. TUnboxedValue convertedValue = TryConvertJson<TValueType, ForceConvert>(valueBuilder, source);
  191. if (!convertedValue) {
  192. // error while converting JSON value type to TValueType
  193. return BuildErrorResult(valueBuilder, TErrorCode::Error, "Cannot convert extracted JSON value to target type");
  194. }
  195. return BuildSuccessfulResult(valueBuilder, std::move(convertedValue));
  196. } catch (const std::exception& e) {
  197. UdfTerminate((TStringBuilder() << Pos_ << " " << e.what()).data());
  198. }
  199. }
  200. TSourcePosition Pos_;
  201. };
  202. template <EDataSlot InputType, class TValueType, bool ForceConvert>
  203. TStringRef TSqlValue<InputType, TValueType, ForceConvert>::Name() {
  204. Y_ABORT("Unknown name");
  205. }
  206. template<>
  207. TStringRef TSqlValue<EDataSlot::Json, TUtf8, true>::Name() {
  208. return TStringRef::Of("SqlValueConvertToUtf8");
  209. }
  210. template <>
  211. TStringRef TSqlValue<EDataSlot::Json, TUtf8>::Name() {
  212. return TStringRef::Of("SqlValueUtf8");
  213. }
  214. template <>
  215. TStringRef TSqlValue<EDataSlot::Json, i64>::Name() {
  216. return TStringRef::Of("SqlValueInt64");
  217. }
  218. template <>
  219. TStringRef TSqlValue<EDataSlot::Json, double>::Name() {
  220. return TStringRef::Of("SqlValueNumber");
  221. }
  222. template <>
  223. TStringRef TSqlValue<EDataSlot::Json, bool>::Name() {
  224. return TStringRef::Of("SqlValueBool");
  225. }
  226. template<>
  227. TStringRef TSqlValue<EDataSlot::JsonDocument, TUtf8, true>::Name() {
  228. return TStringRef::Of("JsonDocumentSqlValueConvertToUtf8");
  229. }
  230. template <>
  231. TStringRef TSqlValue<EDataSlot::JsonDocument, TUtf8>::Name() {
  232. return TStringRef::Of("JsonDocumentSqlValueUtf8");
  233. }
  234. template <>
  235. TStringRef TSqlValue<EDataSlot::JsonDocument, i64>::Name() {
  236. return TStringRef::Of("JsonDocumentSqlValueInt64");
  237. }
  238. template <>
  239. TStringRef TSqlValue<EDataSlot::JsonDocument, double>::Name() {
  240. return TStringRef::Of("JsonDocumentSqlValueNumber");
  241. }
  242. template <>
  243. TStringRef TSqlValue<EDataSlot::JsonDocument, bool>::Name() {
  244. return TStringRef::Of("JsonDocumentSqlValueBool");
  245. }
  246. }