peel.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #include "peel.h"
  2. #include "node.h"
  3. #include "yson.h"
  4. #include "json.h"
  5. #include "convert.h"
  6. #include <yql/essentials/public/udf/udf_type_inspection.h>
  7. #include <yql/essentials/public/udf/udf_type_printer.h>
  8. namespace NYql::NDom {
  9. using namespace NUdf;
  10. namespace {
  11. template<bool Strict, bool AutoConvert>
  12. TUnboxedValuePod PeelData(const TDataTypeId nodeType, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  13. switch (nodeType) {
  14. case TDataType<char*>::Id: return ConvertToString<Strict, AutoConvert, false>(value, valueBuilder, pos);
  15. case TDataType<TUtf8>::Id: return ConvertToString<Strict, AutoConvert, true>(value, valueBuilder, pos);
  16. case TDataType<bool>::Id: return ConvertToBool<Strict, AutoConvert>(value, valueBuilder, pos);
  17. case TDataType<i8>::Id: return ConvertToIntegral<Strict, AutoConvert, i8>(value, valueBuilder, pos);
  18. case TDataType<i16>::Id: return ConvertToIntegral<Strict, AutoConvert, i16>(value, valueBuilder, pos);
  19. case TDataType<i32>::Id: return ConvertToIntegral<Strict, AutoConvert, i32>(value, valueBuilder, pos);
  20. case TDataType<i64>::Id: return ConvertToIntegral<Strict, AutoConvert, i64>(value, valueBuilder, pos);
  21. case TDataType<ui8>::Id: return ConvertToIntegral<Strict, AutoConvert, ui8>(value, valueBuilder, pos);
  22. case TDataType<ui16>::Id: return ConvertToIntegral<Strict, AutoConvert, ui16>(value, valueBuilder, pos);
  23. case TDataType<ui32>::Id: return ConvertToIntegral<Strict, AutoConvert, ui32>(value, valueBuilder, pos);
  24. case TDataType<ui64>::Id: return ConvertToIntegral<Strict, AutoConvert, ui64>(value, valueBuilder, pos);
  25. case TDataType<float>::Id: return ConvertToFloat<Strict, AutoConvert, float>(value, valueBuilder, pos);
  26. case TDataType<double>::Id: return ConvertToFloat<Strict, AutoConvert, double>(value, valueBuilder, pos);
  27. case TDataType<TYson>::Id: return valueBuilder->NewString(SerializeYsonDomToBinary(value)).Release();
  28. case TDataType<TJson>::Id: return valueBuilder->NewString(SerializeJsonDom(value)).Release();
  29. default: break;
  30. }
  31. UdfTerminate((::TStringBuilder() << pos << " Unsupported data type: " << static_cast<int>(nodeType)).c_str());
  32. }
  33. template<bool Strict, bool AutoConvert>
  34. TUnboxedValuePod TryPeelDom(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos);
  35. template<bool Strict, bool AutoConvert>
  36. TUnboxedValuePod PeelList(const ITypeInfoHelper* typeHelper, const TType* itemType, const TUnboxedValuePod x, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  37. switch (GetNodeType(x)) {
  38. case ENodeType::List: {
  39. if (!x.IsBoxed())
  40. break;
  41. if constexpr (Strict || AutoConvert) {
  42. return TUnboxedValuePod(new TLazyConveter(x, std::bind(&PeelDom<Strict, AutoConvert>, typeHelper, itemType, std::placeholders::_1, valueBuilder, pos)));
  43. }
  44. TSmallVec<TUnboxedValue, TUnboxedValue::TAllocator> values;
  45. if (const auto elements = x.GetElements()) {
  46. const auto size = x.GetListLength();
  47. values.reserve(size);
  48. for (ui32 i = 0U; i < size; ++i) {
  49. if (const auto item = TryPeelDom<Strict, AutoConvert>(typeHelper, itemType, elements[i], valueBuilder, pos))
  50. values.emplace_back(item.GetOptionalValue());
  51. else if constexpr (Strict)
  52. UdfTerminate((::TStringBuilder() << pos << " Error on convert list item.").c_str());
  53. }
  54. } else {
  55. const auto it = x.GetListIterator();
  56. for (TUnboxedValue v; it.Next(v);) {
  57. if (const auto item = TryPeelDom<Strict, AutoConvert>(typeHelper, itemType, v, valueBuilder, pos))
  58. values.emplace_back(item.GetOptionalValue());
  59. else if constexpr (Strict)
  60. UdfTerminate((::TStringBuilder() << pos << " Error on convert list item.").c_str());
  61. }
  62. }
  63. if (values.empty()) {
  64. break;
  65. }
  66. return valueBuilder->NewList(values.data(), values.size()).Release();
  67. }
  68. case ENodeType::Attr:
  69. return PeelList<Strict, AutoConvert>(typeHelper, itemType, x.GetVariantItem().Release(), valueBuilder, pos);
  70. default:
  71. if constexpr (AutoConvert)
  72. break;
  73. else if constexpr (Strict)
  74. UdfTerminate((::TStringBuilder() << pos << " Cannot parse list from entity, scalar value or dict.").c_str());
  75. else
  76. return {};
  77. }
  78. return valueBuilder->NewEmptyList().Release();
  79. }
  80. template<bool Strict, bool AutoConvert, bool Utf8Keys>
  81. TUnboxedValuePod PeelDict(const ITypeInfoHelper* typeHelper, const TType* itemType, const TUnboxedValuePod x, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  82. switch (GetNodeType(x)) {
  83. case ENodeType::Dict:
  84. if (!x.IsBoxed())
  85. break;
  86. if constexpr (!Utf8Keys && (Strict || AutoConvert)) {
  87. return TUnboxedValuePod(new TLazyConveter(x, std::bind(&PeelDom<Strict, AutoConvert>, typeHelper, itemType, std::placeholders::_1, valueBuilder, pos)));
  88. }
  89. if (const auto size = x.GetDictLength()) {
  90. TSmallVec<TPair, TStdAllocatorForUdf<TPair>> pairs;
  91. pairs.reserve(size);
  92. const auto it = x.GetDictIterator();
  93. for (TUnboxedValue key, payload; it.NextPair(key, payload);) {
  94. if (const auto k = ConvertToString<Strict, AutoConvert, Utf8Keys>(key.Release(), valueBuilder, pos)) {
  95. if (const auto item = TryPeelDom<Strict, AutoConvert>(typeHelper, itemType, payload, valueBuilder, pos)) {
  96. pairs.emplace_back(std::move(k), item.GetOptionalValue());
  97. continue;
  98. }
  99. }
  100. if constexpr (Strict)
  101. UdfTerminate((::TStringBuilder() << pos << " Error on convert dict payload.").c_str());
  102. }
  103. if (pairs.empty()) {
  104. break;
  105. }
  106. return TUnboxedValuePod(new TMapNode(pairs.data(), pairs.size()));
  107. }
  108. break;
  109. case ENodeType::Attr:
  110. return PeelDict<Strict, AutoConvert, Utf8Keys>(typeHelper, itemType, x.GetVariantItem().Release(), valueBuilder, pos);
  111. default:
  112. if constexpr (AutoConvert)
  113. break;
  114. else if constexpr (Strict)
  115. UdfTerminate((::TStringBuilder() << pos << " Cannot parse dict from entity, scalar value or list.").c_str());
  116. else
  117. return {};
  118. }
  119. return valueBuilder->NewEmptyList().Release();
  120. }
  121. TUnboxedValuePod MakeStub(const ITypeInfoHelper* typeHelper, const TType* shape, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  122. switch (const auto kind = typeHelper->GetTypeKind(shape)) {
  123. case ETypeKind::Optional:
  124. return TUnboxedValuePod();
  125. case ETypeKind::Data:
  126. switch (const auto nodeType = TDataTypeInspector(*typeHelper, shape).GetTypeId()) {
  127. case TDataType<char*>::Id:
  128. case TDataType<TUtf8>::Id:
  129. case TDataType<bool>::Id:
  130. case TDataType<i8>::Id:
  131. case TDataType<i16>::Id:
  132. case TDataType<i32>::Id:
  133. case TDataType<i64>::Id:
  134. case TDataType<ui8>::Id:
  135. case TDataType<ui16>::Id:
  136. case TDataType<ui32>::Id:
  137. case TDataType<ui64>::Id:
  138. case TDataType<float>::Id:
  139. case TDataType<double>::Id:
  140. case TDataType<TDecimal>::Id:
  141. return TUnboxedValuePod::Zero();
  142. case TDataType<TYson>::Id:
  143. return TUnboxedValuePod::Embedded("#");
  144. case TDataType<TJson>::Id:
  145. return TUnboxedValuePod::Embedded("null");
  146. default:
  147. UdfTerminate((::TStringBuilder() << pos << " Unsupported data type: " << static_cast<int>(nodeType)).c_str());
  148. }
  149. case ETypeKind::Tuple:
  150. if (const auto tupleTypeInspector = TTupleTypeInspector(*typeHelper, shape); auto count = tupleTypeInspector.GetElementsCount()) {
  151. TUnboxedValue* items = nullptr;
  152. auto result = valueBuilder->NewArray(count, items);
  153. items += count;
  154. do *--items = MakeStub(typeHelper, tupleTypeInspector.GetElementType(--count), valueBuilder, pos);
  155. while (count);
  156. return result.Release();
  157. }
  158. return valueBuilder->NewEmptyList().Release();
  159. case ETypeKind::Struct:
  160. if (const auto structTypeInspector = TStructTypeInspector(*typeHelper, shape); auto count = structTypeInspector.GetMembersCount()) {
  161. TUnboxedValue* items = nullptr;
  162. auto result = valueBuilder->NewArray(count, items);
  163. items += count;
  164. do *--items = MakeStub(typeHelper, structTypeInspector.GetMemberType(--count), valueBuilder, pos);
  165. while (count);
  166. return result.Release();
  167. }
  168. return valueBuilder->NewEmptyList().Release();
  169. case ETypeKind::List:
  170. case ETypeKind::Dict:
  171. return valueBuilder->NewEmptyList().Release();
  172. case ETypeKind::Resource:
  173. if (const auto inspector = TResourceTypeInspector(*typeHelper, shape); TStringBuf(inspector.GetTag()) == NodeResourceName)
  174. return MakeEntity();
  175. [[fallthrough]];
  176. default:
  177. UdfTerminate((::TStringBuilder() << pos << " Unsupported data kind: " << kind).c_str());
  178. }
  179. }
  180. template<bool Strict, bool AutoConvert>
  181. TUnboxedValuePod PeelTuple(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod x, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  182. if (const auto tupleTypeInspector = TTupleTypeInspector(*typeHelper, shape); auto count = tupleTypeInspector.GetElementsCount()) {
  183. switch (GetNodeType(x)) {
  184. case ENodeType::List: {
  185. TUnboxedValue* items = nullptr;
  186. auto result = valueBuilder->NewArray(count, items);
  187. ui32 i = 0U;
  188. if (x.IsBoxed()) {
  189. if (auto elements = x.GetElements()) {
  190. for (auto size = x.GetListLength(); count && size--; --count) {
  191. if (const auto item = TryPeelDom<Strict, AutoConvert>(typeHelper, tupleTypeInspector.GetElementType(i++), *elements++, valueBuilder, pos))
  192. *items++ = item.GetOptionalValue();
  193. else if constexpr (Strict)
  194. UdfTerminate((::TStringBuilder() << pos << " Error on convert tuple item.").c_str());
  195. else
  196. return {};
  197. }
  198. } else if (const auto it = x.GetListIterator()) {
  199. for (TUnboxedValue v; count && it.Next(v); --count) {
  200. if (const auto item = TryPeelDom<Strict, AutoConvert>(typeHelper, tupleTypeInspector.GetElementType(i++), v, valueBuilder, pos))
  201. *items++ = item.GetOptionalValue();
  202. else if constexpr (Strict)
  203. UdfTerminate((::TStringBuilder() << pos << " Error on convert tuple item.").c_str());
  204. else
  205. return {};
  206. }
  207. }
  208. }
  209. if (count) do
  210. if constexpr (AutoConvert)
  211. *items++ = MakeStub(typeHelper, tupleTypeInspector.GetElementType(i++), valueBuilder, pos);
  212. else if (ETypeKind::Optional == typeHelper->GetTypeKind(tupleTypeInspector.GetElementType(i++)))
  213. ++items;
  214. else if constexpr (Strict)
  215. UdfTerminate((::TStringBuilder() << pos << " DOM list has less items then " << tupleTypeInspector.GetElementsCount() << " tuple elements.").c_str());
  216. else
  217. return {};
  218. while (--count);
  219. return result.Release();
  220. }
  221. case ENodeType::Attr:
  222. return PeelTuple<Strict, AutoConvert>(typeHelper, shape, x.GetVariantItem().Release(), valueBuilder, pos);
  223. default:
  224. if constexpr (AutoConvert) {
  225. TUnboxedValue* items = nullptr;
  226. auto result = valueBuilder->NewArray(count, items);
  227. for (ui32 i = 0ULL; i < count; ++i)
  228. if (ETypeKind::Optional != typeHelper->GetTypeKind(tupleTypeInspector.GetElementType(i)))
  229. *items++ = MakeStub(typeHelper, tupleTypeInspector.GetElementType(i), valueBuilder, pos);
  230. else
  231. ++items;
  232. return result.Release();
  233. } else if constexpr (Strict)
  234. UdfTerminate((::TStringBuilder() << pos << " Cannot parse tuple from entity, scalar value or dict.").c_str());
  235. else
  236. break;
  237. }
  238. }
  239. return {};
  240. }
  241. template<bool Strict, bool AutoConvert>
  242. TUnboxedValuePod PeelStruct(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod x, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  243. if (const auto structTypeInspector = TStructTypeInspector(*typeHelper, shape)) {
  244. const auto size = structTypeInspector.GetMembersCount();
  245. switch (GetNodeType(x)) {
  246. case ENodeType::Dict: {
  247. TUnboxedValue* items = nullptr;
  248. auto result = valueBuilder->NewArray(size, items);
  249. for (ui32 i = 0ULL; i < size; ++i) {
  250. if (x.IsBoxed()) {
  251. if (const auto v = x.Lookup(valueBuilder->NewString(structTypeInspector.GetMemberName(i)))) {
  252. if (const auto item = TryPeelDom<Strict, AutoConvert>(typeHelper, structTypeInspector.GetMemberType(i), v.GetOptionalValue(), valueBuilder, pos))
  253. *items++ = item.GetOptionalValue();
  254. else if constexpr (Strict)
  255. UdfTerminate((::TStringBuilder() << pos << " Error on convert struct member '" << structTypeInspector.GetMemberName(i) << "'.").c_str());
  256. else
  257. return {};
  258. continue;
  259. }
  260. }
  261. if constexpr (AutoConvert)
  262. *items++ = MakeStub(typeHelper, structTypeInspector.GetMemberType(i), valueBuilder, pos);
  263. else if (ETypeKind::Optional == typeHelper->GetTypeKind(structTypeInspector.GetMemberType(i)))
  264. ++items;
  265. else if constexpr (Strict)
  266. UdfTerminate((::TStringBuilder() << pos << " Missed struct member '" << structTypeInspector.GetMemberName(i) << "'.").c_str());
  267. else
  268. return {};
  269. }
  270. return result.Release();
  271. }
  272. case ENodeType::Attr:
  273. return PeelStruct<Strict, AutoConvert>(typeHelper, shape, x.GetVariantItem().Release(), valueBuilder, pos);
  274. default:
  275. if constexpr (AutoConvert) {
  276. TUnboxedValue* items = nullptr;
  277. auto result = valueBuilder->NewArray(size, items);
  278. for (ui32 i = 0ULL; i < size; ++i)
  279. if (ETypeKind::Optional != typeHelper->GetTypeKind(structTypeInspector.GetMemberType(i)))
  280. *items++ = MakeStub(typeHelper, structTypeInspector.GetMemberType(i), valueBuilder, pos);
  281. else
  282. ++items;
  283. return result.Release();
  284. } else if constexpr (Strict)
  285. UdfTerminate((::TStringBuilder() << pos << " Cannot parse struct from entity, scalar value or list.").c_str());
  286. else
  287. break;
  288. }
  289. }
  290. return {};
  291. }
  292. template<bool Strict, bool AutoConvert>
  293. TUnboxedValuePod PeelOptional(const ITypeInfoHelper* typeHelper, const TType* itemType, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  294. if (IsNodeType<ENodeType::Entity>(value))
  295. return TUnboxedValuePod().MakeOptional();
  296. if (const auto result = TryPeelDom<Strict, AutoConvert>(typeHelper, itemType, value, valueBuilder, pos); AutoConvert || result)
  297. return result;
  298. else if constexpr (Strict)
  299. UdfTerminate((::TStringBuilder() << pos << " Failed to convert Yson DOM.").c_str());
  300. else
  301. return TUnboxedValuePod().MakeOptional();
  302. }
  303. template<bool Strict, bool AutoConvert>
  304. TUnboxedValuePod TryPeelDom(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  305. switch (const auto kind = typeHelper->GetTypeKind(shape)) {
  306. case ETypeKind::Data:
  307. return PeelData<Strict, AutoConvert>(TDataTypeInspector(*typeHelper, shape).GetTypeId(), value, valueBuilder, pos);
  308. case ETypeKind::Optional:
  309. return PeelOptional<Strict, AutoConvert>(typeHelper, TOptionalTypeInspector(*typeHelper, shape).GetItemType(), value, valueBuilder, pos);
  310. case ETypeKind::List:
  311. return PeelList<Strict, AutoConvert>(typeHelper, TListTypeInspector(*typeHelper, shape).GetItemType(), value, valueBuilder, pos);
  312. case ETypeKind::Dict: {
  313. const auto dictTypeInspector = TDictTypeInspector(*typeHelper, shape);
  314. const auto keyType = dictTypeInspector.GetKeyType();
  315. if (const auto keyKind = typeHelper->GetTypeKind(keyType); ETypeKind::Data == keyKind)
  316. switch (const auto keyId = TDataTypeInspector(*typeHelper, keyType).GetTypeId()) {
  317. case TDataType<char*>::Id: return PeelDict<Strict, AutoConvert, false>(typeHelper, dictTypeInspector.GetValueType(), value, valueBuilder, pos);
  318. case TDataType<TUtf8>::Id: return PeelDict<Strict, AutoConvert, true>(typeHelper, dictTypeInspector.GetValueType(), value, valueBuilder, pos);
  319. default: UdfTerminate((::TStringBuilder() << pos << " Unsupported dict key type: " << keyId).c_str());
  320. }
  321. else
  322. UdfTerminate((::TStringBuilder() << pos << " Unsupported dict key kind: " << keyKind).c_str());
  323. }
  324. case ETypeKind::Tuple:
  325. return PeelTuple<Strict, AutoConvert>(typeHelper, shape, value, valueBuilder, pos);
  326. case ETypeKind::Struct:
  327. return PeelStruct<Strict, AutoConvert>(typeHelper, shape, value, valueBuilder, pos);
  328. case ETypeKind::Resource:
  329. if (const auto inspector = TResourceTypeInspector(*typeHelper, shape); TStringBuf(inspector.GetTag()) == NodeResourceName)
  330. return value;
  331. [[fallthrough]]; // AUTOGENERATED_FALLTHROUGH_FIXME
  332. default:
  333. UdfTerminate((::TStringBuilder() << pos << " Unsupported data kind: " << kind).c_str());
  334. }
  335. }
  336. }
  337. template<bool Strict, bool AutoConvert>
  338. TUnboxedValuePod PeelDom(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  339. if (const auto result = TryPeelDom<Strict, AutoConvert>(typeHelper, shape, value, valueBuilder, pos))
  340. return result.GetOptionalValue();
  341. ::TStringBuilder sb;
  342. sb << pos << " Failed to convert Yson DOM into strict type: ";
  343. TTypePrinter(*typeHelper, shape).Out(sb.Out);
  344. UdfTerminate(sb.c_str());
  345. }
  346. template TUnboxedValuePod PeelDom<true, true>(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos);
  347. template TUnboxedValuePod PeelDom<false, true>(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos);
  348. template TUnboxedValuePod PeelDom<true, false>(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos);
  349. template TUnboxedValuePod PeelDom<false, false>(const ITypeInfoHelper* typeHelper, const TType* shape, const TUnboxedValuePod value, const IValueBuilder* valueBuilder, const TSourcePosition& pos);
  350. }