mkql_validate_ut.cpp 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
  1. #include <yql/essentials/minikql/mkql_program_builder.h>
  2. #include <yql/essentials/minikql/mkql_node_printer.h>
  3. #include "mkql_computation_list_adapter.h"
  4. #include "mkql_computation_node_impl.h"
  5. #include "mkql_computation_node.h"
  6. #include "mkql_value_builder.h"
  7. #include <yql/essentials/minikql/mkql_function_registry.h>
  8. #include <yql/essentials/minikql/mkql_string_util.h>
  9. #include <yql/essentials/minikql/invoke_builtins/mkql_builtins.h>
  10. #include "mkql_validate.h"
  11. #include <yql/essentials/minikql/mkql_type_builder.h>
  12. #include <yql/essentials/minikql/mkql_utils.h>
  13. #include <yql/essentials/minikql/comp_nodes/mkql_factories.h>
  14. #include <library/cpp/testing/unittest/registar.h>
  15. #include <util/generic/algorithm.h>
  16. #include <yql/essentials/public/udf/udf_helpers.h>
  17. namespace NYql {
  18. namespace {
  19. using namespace NKikimr::NMiniKQL;
  20. static const ui32 RAW_INDEX_NO_HOLE = -1;
  21. static const ui32 RAW_BROKEN_INDEX_LIST_TO_DICT = 1;
  22. template<class T>
  23. NUdf::TUnboxedValue ToUnboxedValue(const T& val) {
  24. return NUdf::TUnboxedValuePod(val);
  25. }
  26. NUdf::TUnboxedValue ToUnboxedValue(const TString& val) {
  27. return MakeString(val);
  28. }
  29. NUdf::TUnboxedValue ToUnboxedValue(const NUdf::IBoxedValuePtr& val) {
  30. return NUdf::TUnboxedValuePod(NUdf::IBoxedValuePtr(val));
  31. }
  32. } // namespace NMiniKQL
  33. /// support for build Struct type @{
  34. namespace NUdf {
  35. template<class TContainer>
  36. struct TListRefIterator: public TBoxedValue {
  37. TListRefIterator(const TContainer& listRef, ui32 holePos)
  38. : ListRef(listRef)
  39. , Index(-1)
  40. , HolePos(holePos)
  41. {}
  42. private:
  43. const TContainer& ListRef;
  44. ui32 Index;
  45. ui32 HolePos;
  46. bool Next(NUdf::TUnboxedValue& value) final {
  47. if (++Index >= ListRef.size())
  48. return false;
  49. value = Index == HolePos ? NUdf::TUnboxedValue(NUdf::TUnboxedValuePod(42)) : ToUnboxedValue(ListRef[Index]);
  50. return true;
  51. }
  52. };
  53. template<class TContainer, ui32 TIndexDictBrokenHole = RAW_INDEX_NO_HOLE, bool TNoDictIndex = false>
  54. struct TListRef: public NUdf::TBoxedValue {
  55. TListRef(const TContainer& listRef, ui32 holePos = RAW_INDEX_NO_HOLE)
  56. : ListRef(listRef)
  57. , HolePos(holePos)
  58. {}
  59. private:
  60. const TContainer& ListRef;
  61. const NUdf::IValueBuilder* ValueBuilder;
  62. ui32 HolePos;
  63. bool HasFastListLength() const override {
  64. return true;
  65. }
  66. ui64 GetListLength() const override {
  67. return ListRef.size();
  68. }
  69. ui64 GetEstimatedListLength() const override {
  70. return ListRef.size();
  71. }
  72. NUdf::TUnboxedValue GetListIterator() const override {
  73. return NUdf::TUnboxedValuePod(new TListRefIterator<TContainer>(ListRef, HolePos));
  74. }
  75. NUdf::IBoxedValuePtr ToIndexDictImpl(const IValueBuilder& builder) const override {
  76. return TNoDictIndex ? nullptr : builder.ToIndexDict(NUdf::TUnboxedValuePod(
  77. new TListRef<TContainer, TIndexDictBrokenHole, true>(ListRef, TIndexDictBrokenHole))).AsBoxed();
  78. }
  79. };
  80. struct PersonStruct {
  81. static const size_t MEMBERS_COUNT = 3;
  82. static ui32 MetaIndexes[MEMBERS_COUNT];
  83. static ui32 MetaBackIndexes[MEMBERS_COUNT];
  84. TString FirstName;
  85. TString LastName;
  86. ui32 Age;
  87. NUdf::TUnboxedValue GetByIndex(ui32 index) const {
  88. switch (index) {
  89. case 0: return ToUnboxedValue(FirstName);
  90. case 1: return ToUnboxedValue(LastName);
  91. case 2: return NUdf::TUnboxedValuePod(Age);
  92. default: Y_ABORT("Unexpected");
  93. }
  94. }
  95. };
  96. ui32 PersonStruct::MetaIndexes[MEMBERS_COUNT];
  97. ui32 PersonStruct::MetaBackIndexes[MEMBERS_COUNT];
  98. struct PersonStructWithOptList {
  99. static const size_t MEMBERS_COUNT = 4;
  100. static ui32 MetaIndexes[MEMBERS_COUNT];
  101. static ui32 MetaBackIndexes[MEMBERS_COUNT];
  102. TString FirstName;
  103. TString LastName;
  104. ui32 Age;
  105. typedef std::vector<ui32> TTagList;
  106. TTagList Tags;
  107. NUdf::TUnboxedValue GetByIndex(ui32 index) const {
  108. switch (index) {
  109. case 0: return ToUnboxedValue(FirstName);
  110. case 1: return ToUnboxedValue(LastName);
  111. case 2: return NUdf::TUnboxedValuePod(Age);
  112. case 3: return Tags.empty() ?
  113. NUdf::TUnboxedValuePod() :
  114. NUdf::TUnboxedValuePod(new TListRef<TTagList>(Tags));
  115. default: Y_ABORT("Unexpected");
  116. }
  117. }
  118. };
  119. ui32 PersonStructWithOptList::MetaIndexes[MEMBERS_COUNT];
  120. ui32 PersonStructWithOptList::MetaBackIndexes[MEMBERS_COUNT];
  121. struct TCallableOneUi32Arg {
  122. };
  123. namespace NImpl {
  124. template<>
  125. struct TTypeBuilderHelper<NUdf::PersonStruct> {
  126. static TType* Build(const IFunctionTypeInfoBuilder& builder) {
  127. auto structBuilder = builder.Struct(3);
  128. structBuilder->AddField<char*>("FirstName", &PersonStruct::MetaIndexes[0])
  129. .AddField<char*>("LastName", &PersonStruct::MetaIndexes[1])
  130. .AddField<ui32>("Age", &PersonStruct::MetaIndexes[2]);
  131. auto structType = structBuilder->Build();
  132. for (const auto& index: PersonStruct::MetaIndexes) {
  133. Y_ABORT_UNLESS(index < NUdf::PersonStruct::MEMBERS_COUNT);
  134. NUdf::PersonStruct::MetaBackIndexes[index] = &index - PersonStruct::MetaIndexes;
  135. Y_ABORT_UNLESS(NUdf::PersonStruct::MetaBackIndexes[index] < NUdf::PersonStruct::MEMBERS_COUNT);
  136. }
  137. return structType;
  138. }
  139. };
  140. template<>
  141. struct TTypeBuilderHelper<NUdf::PersonStructWithOptList> {
  142. static TType* Build(const IFunctionTypeInfoBuilder& builder) {
  143. auto listTags = builder.List()->Item<ui32>().Build();
  144. auto optionalListTags = builder.Optional()->Item(listTags).Build();
  145. auto structBuilder = builder.Struct(3);
  146. structBuilder->AddField<char*>("FirstName", &PersonStructWithOptList::MetaIndexes[0])
  147. .AddField<char*>("LastName", &PersonStructWithOptList::MetaIndexes[1])
  148. .AddField<ui32>("Age", &PersonStructWithOptList::MetaIndexes[2])
  149. .AddField("Tags", optionalListTags, &PersonStructWithOptList::MetaIndexes[3]);
  150. auto structType = structBuilder->Build();
  151. for (const auto& index: PersonStructWithOptList::MetaIndexes) {
  152. Y_ABORT_UNLESS(index < NUdf::PersonStructWithOptList::MEMBERS_COUNT);
  153. NUdf::PersonStructWithOptList::MetaBackIndexes[index] = &index - PersonStructWithOptList::MetaIndexes;
  154. Y_ABORT_UNLESS(NUdf::PersonStructWithOptList::MetaBackIndexes[index] < NUdf::PersonStructWithOptList::MEMBERS_COUNT);
  155. }
  156. return structType;
  157. }
  158. };
  159. template<>
  160. struct TTypeBuilderHelper<NUdf::TCallableOneUi32Arg> {
  161. static TType* Build(const IFunctionTypeInfoBuilder& builder) {
  162. auto callableBuilder = builder.Callable(1);
  163. callableBuilder->Returns<ui32>();
  164. callableBuilder->Arg<ui32>();
  165. return callableBuilder->Build();
  166. }
  167. };
  168. } // namespace NImpl
  169. } // namespace NUdf
  170. /// @}
  171. struct TBrokenSeqListIterator: public NUdf::TBoxedValue {
  172. TBrokenSeqListIterator(ui32 size, ui32 holePos)
  173. : Size(size)
  174. , HolePos(holePos)
  175. , Index(-1)
  176. {}
  177. private:
  178. ui32 Size;
  179. ui32 HolePos;
  180. ui32 Index;
  181. bool Skip() final {
  182. return ++Index < Size;
  183. }
  184. bool Next(NUdf::TUnboxedValue& value) final {
  185. if (!Skip())
  186. return false;
  187. value = Index == HolePos ? NUdf::TUnboxedValuePod() : NUdf::TUnboxedValuePod(Index);
  188. return true;
  189. }
  190. };
  191. struct TBrokenSeqListBoxedValue: public NUdf::TBoxedValue {
  192. TBrokenSeqListBoxedValue(ui32 size, ui32 holePos)
  193. : ListSize(size)
  194. , HolePos(holePos)
  195. {}
  196. private:
  197. ui32 ListSize;
  198. ui32 HolePos;
  199. bool HasFastListLength() const override {
  200. return true;
  201. }
  202. ui64 GetListLength() const override {
  203. return ListSize;
  204. }
  205. ui64 GetEstimatedListLength() const override {
  206. return ListSize;
  207. }
  208. NUdf::TUnboxedValue GetListIterator() const override {
  209. return NUdf::TUnboxedValuePod(new TBrokenSeqListIterator(ListSize, HolePos));
  210. }
  211. };
  212. template<class TStructType>
  213. struct TBrokenStructBoxedValue: public NUdf::TBoxedValue {
  214. TBrokenStructBoxedValue(const TStructType& data, ui32 holePos = RAW_INDEX_NO_HOLE)
  215. : Struct(data)
  216. , HolePos(holePos)
  217. {}
  218. private:
  219. const TStructType& Struct;
  220. ui32 HolePos;
  221. NUdf::TUnboxedValue GetElement(ui32 index) const override {
  222. if (index == HolePos) {
  223. return NUdf::TUnboxedValuePod();
  224. }
  225. return Struct.GetByIndex(TStructType::MetaBackIndexes[index]);
  226. }
  227. };
  228. namespace {
  229. template<>
  230. NUdf::TUnboxedValue ToUnboxedValue<NUdf::PersonStruct>(const NUdf::PersonStruct& val) {
  231. return NUdf::TUnboxedValuePod(new TBrokenStructBoxedValue<NUdf::PersonStruct>(val));
  232. }
  233. template<>
  234. NUdf::TUnboxedValue ToUnboxedValue<NUdf::PersonStructWithOptList>(const NUdf::PersonStructWithOptList& val) {
  235. return NUdf::TUnboxedValuePod(new TBrokenStructBoxedValue<NUdf::PersonStructWithOptList>(val));
  236. }
  237. template<class TTupleType>
  238. struct TBrokenTupleBoxedValue: public NUdf::TBoxedValue {
  239. TBrokenTupleBoxedValue(const TTupleType& tuple, ui32 holePos)
  240. : Tuple(tuple)
  241. , HolePos(holePos)
  242. {}
  243. private:
  244. const TTupleType& Tuple;
  245. ui32 HolePos;
  246. NUdf::TUnboxedValue GetElement(ui32 index) const override {
  247. if (index == HolePos) {
  248. return NUdf::TUnboxedValuePod();
  249. }
  250. switch (index) {
  251. case 0: return ToUnboxedValue(std::get<0>(Tuple));
  252. case 1: return ToUnboxedValue(std::get<1>(Tuple));
  253. case 2: return ToUnboxedValue(std::get<2>(Tuple));
  254. case 3: return ToUnboxedValue(std::get<3>(Tuple));
  255. default: Y_ABORT("Unexpected");
  256. }
  257. }
  258. };
  259. typedef std::pair<ui32, ui32> PosPair;
  260. template<class TKey, class TValue>
  261. struct TBrokenDictIterator: public NUdf::TBoxedValue {
  262. TBrokenDictIterator(const std::vector<std::pair<TKey, TValue>>& dictData, PosPair holePos)
  263. : DictData(dictData)
  264. , HolePos(holePos)
  265. , Index(-1)
  266. {}
  267. private:
  268. const std::vector<std::pair<TKey, TValue>>& DictData;
  269. PosPair HolePos;
  270. ui32 Index;
  271. bool Skip() final {
  272. return ++Index < DictData.size();
  273. }
  274. bool Next(NUdf::TUnboxedValue& key) final {
  275. if (!Skip())
  276. return false;
  277. key = Index == HolePos.first ? NUdf::TUnboxedValuePod() : NUdf::TUnboxedValuePod(DictData[Index].first);
  278. return true;
  279. }
  280. bool NextPair(NUdf::TUnboxedValue& key, NUdf::TUnboxedValue& payload) final {
  281. if (!Next(key))
  282. return false;
  283. payload = Index == HolePos.second ? NUdf::TUnboxedValue() : ToUnboxedValue(DictData[Index].second);
  284. return true;
  285. }
  286. };
  287. template<class TKey, class TValue>
  288. struct TBrokenDictBoxedValue: public NUdf::TBoxedValue {
  289. TBrokenDictBoxedValue(const std::vector<std::pair<TKey, TValue>>& dictData,
  290. PosPair holePos, NUdf::TUnboxedValue&& hole = NUdf::TUnboxedValuePod())
  291. : DictData(dictData)
  292. , HolePos(holePos)
  293. , Hole(std::move(hole))
  294. {}
  295. private:
  296. const std::vector<std::pair<TKey, TValue>> DictData;
  297. PosPair HolePos;
  298. NUdf::TUnboxedValue Hole;
  299. NUdf::TUnboxedValue GetKeysIterator() const override {
  300. return NUdf::TUnboxedValuePod(new TBrokenDictIterator<TKey, TValue>(DictData, HolePos));
  301. }
  302. NUdf::TUnboxedValue GetDictIterator() const override {
  303. return NUdf::TUnboxedValuePod(new TBrokenDictIterator<TKey, TValue>(DictData, HolePos));
  304. }
  305. };
  306. struct TThrowerValue: public NUdf::TBoxedValue {
  307. static long Count;
  308. TThrowerValue(NUdf::IBoxedValuePtr&& owner = NUdf::IBoxedValuePtr())
  309. : Owner(std::move(owner))
  310. { ++Count; }
  311. ~TThrowerValue() { --Count; }
  312. private:
  313. const NUdf::IBoxedValuePtr Owner;
  314. bool Skip() override {
  315. ythrow yexception() << "Throw";
  316. }
  317. NUdf::TUnboxedValue GetListIterator() const override {
  318. return NUdf::TUnboxedValuePod(new TThrowerValue(const_cast<TThrowerValue*>(this)));
  319. }
  320. };
  321. SIMPLE_UDF(TException, NUdf::TListType<ui32>()) {
  322. Y_UNUSED(valueBuilder);
  323. Y_UNUSED(args);
  324. return NUdf::TUnboxedValuePod(new TThrowerValue);
  325. }
  326. long TThrowerValue::Count = 0L;
  327. SIMPLE_UDF(TVoid, void()) {
  328. Y_UNUSED(valueBuilder);
  329. Y_UNUSED(args);
  330. return NUdf::TUnboxedValuePod::Void();
  331. }
  332. SIMPLE_UDF_RUN(TNonEmpty, ui32(), NUdf::TOptional<void>) {
  333. Y_UNUSED(valueBuilder);
  334. Y_UNUSED(args);
  335. return NUdf::TUnboxedValuePod(42);
  336. }
  337. SIMPLE_UDF(TOptionalNonEmpty, NUdf::TOptional<ui32>()) {
  338. Y_UNUSED(valueBuilder);
  339. Y_UNUSED(args);
  340. return NUdf::TUnboxedValuePod(42);
  341. }
  342. SIMPLE_UDF_RUN(TOptionalEmpty, NUdf::TOptional<ui32>(), NUdf::TOptional<void>) {
  343. Y_UNUSED(valueBuilder);
  344. Y_UNUSED(args);
  345. return NUdf::TUnboxedValuePod();
  346. }
  347. SIMPLE_UDF(TSub2Mul2BrokenOnLess2, ui32(ui32)) {
  348. Y_UNUSED(valueBuilder);
  349. const ui32 arg = args[0].Get<ui32>();
  350. if (arg >= 2) {
  351. return NUdf::TUnboxedValuePod((arg - 2) * 2);
  352. }
  353. return NUdf::TUnboxedValuePod();
  354. }
  355. SIMPLE_UDF_RUN(TBackSub2Mul2, ui32(NUdf::TCallableOneUi32Arg, ui32), NUdf::TOptional<void>) {
  356. const auto func = args[0];
  357. const auto& arg = args[1];
  358. auto usedArg = NUdf::TUnboxedValuePod();
  359. if (arg.Get<ui32>() < 100) {
  360. usedArg = arg;
  361. }
  362. auto funcResult = func.Run(valueBuilder, &usedArg);
  363. const auto& backResult = funcResult.Get<ui32>() / 2 + 2;
  364. return NUdf::TUnboxedValuePod(backResult);
  365. }
  366. SIMPLE_UDF(TSeqList, NUdf::TListType<ui32>(ui32)) {
  367. const ui32 size = args[0].Get<ui32>();
  368. std::vector<NUdf::TUnboxedValue> res;
  369. res.resize(size);
  370. for (ui32 i = 0; i < size; ++i) {
  371. res[i] = NUdf::TUnboxedValuePod(i);
  372. }
  373. return valueBuilder->NewList(res.data(), res.size());
  374. }
  375. SIMPLE_UDF_RUN(TSeqListWithHole, NUdf::TListType<ui32>(ui32, ui32), NUdf::TOptional<void>) {
  376. Y_UNUSED(valueBuilder);
  377. const ui32 size = args[0].Get<ui32>();
  378. const ui32 hole = args[1].Get<ui32>();
  379. NUdf::IBoxedValuePtr boxed(new TBrokenSeqListBoxedValue(size, hole));
  380. return NUdf::TUnboxedValuePod(std::move(boxed));
  381. }
  382. static const auto TUPLE = std::make_tuple(ui8(33), TString("world"), ui64(0xFEEDB00B2A115E), TString("funny bunny"));
  383. typedef NUdf::TTuple<ui8, char*, ui64, char*> NUdfTuple;
  384. SIMPLE_UDF(TTuple, NUdfTuple(ui32)) {
  385. Y_UNUSED(valueBuilder);
  386. const ui32 holePos = args[0].Get<ui32>();
  387. NUdf::IBoxedValuePtr boxed(new TBrokenTupleBoxedValue<decltype(TUPLE)>(TUPLE, holePos));
  388. return NUdf::TUnboxedValuePod(std::move(boxed));
  389. }
  390. static const std::vector<std::pair<ui32, ui64>> DICT_DIGIT2DIGIT = {
  391. {1, 100500},
  392. {42, 0xDEADBEAF},
  393. {911, 1234567890},
  394. {777, 777777777777},
  395. };
  396. typedef NUdf::TDict<ui32, ui64> NUdfDictDigDig;
  397. SIMPLE_UDF_RUN(TDictDigDig, NUdfDictDigDig(ui32, ui32), NUdf::TOptional<void>) {
  398. Y_UNUSED(valueBuilder);
  399. const ui32 holeKey = args[0].Get<ui32>();
  400. const ui32 holeValue = args[1].Get<ui32>();
  401. NUdf::IBoxedValuePtr boxed(new TBrokenDictBoxedValue<ui32, ui64>(
  402. DICT_DIGIT2DIGIT, std::make_pair(holeKey, holeValue)));
  403. return NUdf::TUnboxedValuePod(std::move(boxed));
  404. }
  405. SIMPLE_UDF(TDictDigDigHoleAsOpt, NUdfDictDigDig(ui32, ui32)) {
  406. Y_UNUSED(valueBuilder);
  407. const ui32 holeKey = args[0].Get<ui32>();
  408. const ui32 holeValue = args[1].Get<ui32>();
  409. NUdf::IBoxedValuePtr boxed(new TBrokenDictBoxedValue<ui32, ui64>(DICT_DIGIT2DIGIT,
  410. std::make_pair(holeKey, holeValue),
  411. NUdf::TUnboxedValuePod()));
  412. return NUdf::TUnboxedValuePod(std::move(boxed));
  413. }
  414. static const NUdf::PersonStruct STRUCT_PERSON_JONNIE = {"Johnnie Walker", "Blue Label", 25};
  415. static const NUdf::PersonStruct STRUCT_PERSON_HITHCOCK = {"Alfred", "Hithcock", 81};
  416. static const NUdf::PersonStruct STRUCT_PERSON_LOVECRAFT = {"Howard", "Lovecraft", 25};
  417. static const NUdf::PersonStruct STRUCT_PERSON_KING = {"Stephen", "King", 25};
  418. static const NUdf::PersonStructWithOptList STRUCT_PERSON_HITHCOCK_LIST = {"Alfred", "Hithcock", 81, {}};
  419. static const NUdf::PersonStructWithOptList STRUCT_PERSON_LOVECRAFT_LIST = {"Howard", "Lovecraft", 25, {3, 2, 99}};
  420. static const NUdf::PersonStructWithOptList STRUCT_PERSON_KING_LIST = {"Stephen", "King", 25, {}};
  421. SIMPLE_UDF_RUN(TPersonStruct, NUdf::PersonStruct(ui32), NUdf::TOptional<void>) {
  422. Y_UNUSED(valueBuilder);
  423. const ui32 holePos = args[0].Get<ui32>();
  424. NUdf::IBoxedValuePtr boxed(new TBrokenStructBoxedValue<NUdf::PersonStruct>(STRUCT_PERSON_JONNIE, holePos));
  425. return NUdf::TUnboxedValuePod(std::move(boxed));
  426. }
  427. typedef NUdf::TTuple<NUdf::PersonStructWithOptList,NUdf::PersonStruct,NUdf::PersonStructWithOptList,NUdf::PersonStruct> NUdfPersonTuple;
  428. static const auto TUPLE_OF_PERSON = std::make_tuple(
  429. STRUCT_PERSON_HITHCOCK_LIST,
  430. STRUCT_PERSON_JONNIE,
  431. STRUCT_PERSON_LOVECRAFT_LIST,
  432. STRUCT_PERSON_KING);
  433. static const auto TUPLE_OF_PERSON_NO_LIST = std::make_tuple(STRUCT_PERSON_HITHCOCK_LIST,
  434. STRUCT_PERSON_JONNIE,
  435. STRUCT_PERSON_KING_LIST,
  436. STRUCT_PERSON_KING);
  437. SIMPLE_UDF(TTupleOfPersonStruct, NUdfPersonTuple(ui32)) {
  438. Y_UNUSED(valueBuilder);
  439. const ui32 holePos = args[0].Get<ui32>();
  440. NUdf::IBoxedValuePtr boxed(new TBrokenTupleBoxedValue<decltype(TUPLE_OF_PERSON)>(TUPLE_OF_PERSON, holePos));
  441. return NUdf::TUnboxedValuePod(std::move(boxed));
  442. }
  443. SIMPLE_UDF(TTupleOfPersonStructNoList, NUdfPersonTuple(ui32)) {
  444. Y_UNUSED(valueBuilder);
  445. const ui32 holePos = args[0].Get<ui32>();
  446. NUdf::IBoxedValuePtr boxed(new TBrokenTupleBoxedValue<decltype(TUPLE_OF_PERSON_NO_LIST)>(TUPLE_OF_PERSON_NO_LIST, holePos));
  447. return NUdf::TUnboxedValuePod(std::move(boxed));
  448. }
  449. static const std::vector<NUdf::PersonStructWithOptList> LIST_OF_STRUCT_PERSON = {
  450. STRUCT_PERSON_HITHCOCK_LIST,
  451. STRUCT_PERSON_LOVECRAFT_LIST,
  452. STRUCT_PERSON_KING_LIST
  453. };
  454. typedef NUdf::TDict<ui64,NUdf::PersonStructWithOptList> TIndexDictFromPersonList;
  455. SIMPLE_UDF(TListOfPersonStructToIndexDict, TIndexDictFromPersonList(ui32)) {
  456. Y_UNUSED(valueBuilder);
  457. Y_UNUSED(args);
  458. NUdf::IBoxedValuePtr boxed(new NUdf::TListRef<decltype(LIST_OF_STRUCT_PERSON)>(LIST_OF_STRUCT_PERSON));
  459. return valueBuilder->ToIndexDict(NUdf::TUnboxedValuePod(std::move(boxed)));
  460. }
  461. SIMPLE_UDF(TListOfPersonStruct, NUdf::TListType<NUdf::PersonStructWithOptList>(ui32)) {
  462. Y_UNUSED(valueBuilder);
  463. Y_UNUSED(args);
  464. NUdf::IBoxedValuePtr boxed(new NUdf::TListRef<decltype(LIST_OF_STRUCT_PERSON)>(LIST_OF_STRUCT_PERSON));
  465. return NUdf::TUnboxedValuePod(std::move(boxed));
  466. }
  467. SIMPLE_UDF(TListOfPersonStructWithBrokenIndexToDict, NUdf::TListType<NUdf::PersonStructWithOptList>()) {
  468. Y_UNUSED(valueBuilder);
  469. Y_UNUSED(args);
  470. NUdf::IBoxedValuePtr boxed(new NUdf::TListRef<decltype(LIST_OF_STRUCT_PERSON), RAW_BROKEN_INDEX_LIST_TO_DICT>(
  471. LIST_OF_STRUCT_PERSON));
  472. return NUdf::TUnboxedValuePod(std::move(boxed));
  473. }
  474. static const NUdf::PersonStruct* DICT_DIGIT2PERSON_BROKEN_CONTENT_BY_INDEX[] = {
  475. &STRUCT_PERSON_HITHCOCK, &STRUCT_PERSON_JONNIE, &STRUCT_PERSON_LOVECRAFT
  476. };
  477. const ui32 DICT_DIGIT2PERSON_BROKEN_PERSON_INDEX = 1;
  478. const ui32 DICT_DIGIT2PERSON_BROKEN_STRUCT_INDEX = 2;
  479. const std::vector<std::pair<ui32, NUdf::IBoxedValuePtr>> MAKE_DICT_DIGIT2PERSON_BROKEN() {
  480. std::vector<std::pair<ui32, NUdf::IBoxedValuePtr>> DICT_DIGIT2PERSON_BROKEN = {
  481. { 333, new TBrokenStructBoxedValue<NUdf::PersonStruct>(STRUCT_PERSON_HITHCOCK, RAW_INDEX_NO_HOLE) },
  482. { 5, new TBrokenStructBoxedValue<NUdf::PersonStruct>(STRUCT_PERSON_JONNIE, DICT_DIGIT2PERSON_BROKEN_STRUCT_INDEX) },
  483. { 77, new TBrokenStructBoxedValue<NUdf::PersonStruct>(STRUCT_PERSON_LOVECRAFT, RAW_INDEX_NO_HOLE) },
  484. };
  485. return DICT_DIGIT2PERSON_BROKEN;
  486. }
  487. typedef NUdf::TDict<ui32,NUdf::PersonStruct> NUdfDictDigPerson;
  488. std::vector<std::pair<ui32, NUdf::IBoxedValuePtr>> MAKE_DICT_DIGIT2PERSON() {
  489. const std::vector<std::pair<ui32, NUdf::IBoxedValuePtr>> DICT_DIGIT2PERSON = {
  490. { 333, new TBrokenStructBoxedValue<NUdf::PersonStruct>(STRUCT_PERSON_HITHCOCK, RAW_INDEX_NO_HOLE) },
  491. { 5, new TBrokenStructBoxedValue<NUdf::PersonStruct>(STRUCT_PERSON_JONNIE, RAW_INDEX_NO_HOLE) },
  492. { 77, new TBrokenStructBoxedValue<NUdf::PersonStruct>(STRUCT_PERSON_LOVECRAFT, RAW_INDEX_NO_HOLE) },
  493. };
  494. return DICT_DIGIT2PERSON;
  495. }
  496. SIMPLE_UDF(TDictOfPerson, NUdfDictDigPerson()) {
  497. Y_UNUSED(args);
  498. Y_UNUSED(valueBuilder);
  499. NUdf::IBoxedValuePtr boxed(new TBrokenDictBoxedValue<ui32, NUdf::IBoxedValuePtr>(
  500. MAKE_DICT_DIGIT2PERSON(), std::make_pair(RAW_INDEX_NO_HOLE, RAW_INDEX_NO_HOLE)));
  501. return NUdf::TUnboxedValuePod(std::move(boxed));
  502. }
  503. SIMPLE_UDF_RUN(TDictOfPersonBroken, NUdfDictDigPerson(), NUdf::TOptional<void>) {
  504. Y_UNUSED(args);
  505. Y_UNUSED(valueBuilder);
  506. NUdf::IBoxedValuePtr boxed(new TBrokenDictBoxedValue<ui32, NUdf::IBoxedValuePtr>(
  507. MAKE_DICT_DIGIT2PERSON_BROKEN(), std::make_pair(RAW_INDEX_NO_HOLE, RAW_INDEX_NO_HOLE)));
  508. return NUdf::TUnboxedValuePod(std::move(boxed));
  509. }
  510. SIMPLE_MODULE(TUtUDF,
  511. TException,
  512. TVoid,
  513. TNonEmpty,
  514. TOptionalNonEmpty,
  515. TOptionalEmpty,
  516. TSub2Mul2BrokenOnLess2,
  517. TBackSub2Mul2,
  518. TSeqList,
  519. TSeqListWithHole,
  520. TTuple,
  521. TDictDigDig,
  522. TDictDigDigHoleAsOpt,
  523. TPersonStruct,
  524. TTupleOfPersonStruct,
  525. TTupleOfPersonStructNoList,
  526. TListOfPersonStructToIndexDict,
  527. TListOfPersonStruct,
  528. TListOfPersonStructWithBrokenIndexToDict,
  529. TDictOfPerson,
  530. TDictOfPersonBroken
  531. )
  532. } // unnamed namespace
  533. TIntrusivePtr<IFunctionRegistry> CreateFunctionRegistryWithUDFs() {
  534. auto freg = CreateFunctionRegistry(CreateBuiltinRegistry())->Clone();
  535. freg->AddModule("", "UtUDF", new TUtUDF());
  536. return freg;
  537. }
  538. Y_UNIT_TEST_SUITE(TMiniKQLValidateTest) {
  539. typedef std::function<std::vector<TRuntimeNode>(TProgramBuilder&)> BuildArgsFunc;
  540. typedef std::function<void(const NUdf::TUnboxedValuePod&, const NUdf::IValueBuilder*)> ValidateValueFunc;
  541. typedef std::function<void(const NUdf::TUnboxedValuePod&, const NUdf::IValueBuilder*, const TType* type)> FullValidateValueFunc;
  542. void ProcessSimpleUdfFunc(const char* udfFuncName, BuildArgsFunc argsFunc = BuildArgsFunc(), ValidateValueFunc validateFunc = ValidateValueFunc(),
  543. FullValidateValueFunc fullValidateFunc = FullValidateValueFunc(),
  544. NUdf::EValidateMode validateMode = NUdf::EValidateMode::Lazy) {
  545. TScopedAlloc alloc(__LOCATION__);
  546. TTypeEnvironment env(alloc);
  547. NUdf::ITypeInfoHelper::TPtr typeInfoHelper(new TTypeInfoHelper);
  548. auto functionRegistry = CreateFunctionRegistryWithUDFs();
  549. auto randomProvider = CreateDeterministicRandomProvider(1);
  550. auto timeProvider = CreateDeterministicTimeProvider(10000000);
  551. TProgramBuilder pgmBuilder(env, *functionRegistry);
  552. auto funcName = pgmBuilder.Udf(udfFuncName);
  553. std::vector<TRuntimeNode> execArgs;
  554. if (argsFunc) {
  555. execArgs = argsFunc(pgmBuilder);
  556. }
  557. auto pgmReturn = pgmBuilder.Apply(funcName, execArgs);
  558. TExploringNodeVisitor explorer;
  559. explorer.Walk(pgmReturn.GetNode(), env);
  560. TComputationPatternOpts opts(alloc.Ref(), env, GetBuiltinFactory(),
  561. functionRegistry.Get(), validateMode,
  562. NUdf::EValidatePolicy::Exception, "OFF", EGraphPerProcess::Multi);
  563. auto pattern = MakeComputationPattern(explorer, pgmReturn, {}, opts);
  564. auto graph = pattern->Clone(opts.ToComputationOptions(*randomProvider, *timeProvider));
  565. const auto builder = static_cast<TDefaultValueBuilder*>(graph->GetTerminator());
  566. builder->RethrowAtTerminate();
  567. const TBindTerminator bind(graph->GetTerminator());
  568. auto value = graph->GetValue();
  569. if (validateFunc) {
  570. validateFunc(value, builder);
  571. }
  572. if (fullValidateFunc) {
  573. ui32 flags = 0;
  574. TFunctionTypeInfo funcInfo;
  575. TType* userType = nullptr;
  576. TStringBuf typeConfig;
  577. TStatus status = functionRegistry->FindFunctionTypeInfo(env, typeInfoHelper, nullptr, udfFuncName, userType, typeConfig, flags, {}, nullptr, &funcInfo);
  578. MKQL_ENSURE(status.IsOk(), status.GetError());
  579. auto type = funcInfo.FunctionType->GetReturnType();
  580. fullValidateFunc(value, builder, type);
  581. }
  582. }
  583. Y_UNIT_TEST(TestUdfException) {
  584. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  585. valueBuilder->NewStringNotFilled(0xBAD).AsStringValue().Ref(); // Leak string.
  586. NUdf::TBoxedValueAccessor::Skip(*value.GetListIterator().AsBoxed().Release()); // Leak value and throw exception.
  587. };
  588. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.Exception", {}, validateFunc), yexception);
  589. UNIT_ASSERT_VALUES_EQUAL(TThrowerValue::Count, 0L);
  590. }
  591. Y_UNIT_TEST(TestUdfResultCheckVoid) {
  592. ProcessSimpleUdfFunc("UtUDF.Void");
  593. }
  594. Y_UNIT_TEST(TestUdfResultCheckExceptionOnEmpty) {
  595. TScopedAlloc alloc(__LOCATION__);
  596. TTypeEnvironment env(alloc);
  597. bool wrapped = false;
  598. UNIT_ASSERT_EXCEPTION(TValidate<TValidateErrorPolicyThrow>::Value(nullptr, env.GetTypeOfTypeLazy(),
  599. NUdf::TUnboxedValuePod(), "ut for verify empty value exception", &wrapped), TUdfValidateException);
  600. UNIT_ASSERT(!wrapped);
  601. }
  602. Y_UNIT_TEST(TestUdfResultCheckNonEmpty) {
  603. ProcessSimpleUdfFunc("UtUDF.NonEmpty");
  604. }
  605. Y_UNIT_TEST(TestUdfResultCheckOptionalNonEmpty) {
  606. ProcessSimpleUdfFunc("UtUDF.OptionalNonEmpty");
  607. }
  608. Y_UNIT_TEST(TestUdfResultCheckOptionalEmpty) {
  609. ProcessSimpleUdfFunc("UtUDF.OptionalEmpty");
  610. }
  611. std::vector<TRuntimeNode> MakeCallableInArgs(ui32 testVal, TProgramBuilder& pgmBuilder) {
  612. const auto& functionRegistry = pgmBuilder.GetFunctionRegistry();
  613. const auto udfFuncName = "UtUDF.Sub2Mul2BrokenOnLess2";
  614. ui32 flags = 0;
  615. TFunctionTypeInfo funcInfo;
  616. TType* userType = nullptr;
  617. TStringBuf typeConfig;
  618. NUdf::ITypeInfoHelper::TPtr typeInfoHelper(new TTypeInfoHelper);
  619. TStatus status = functionRegistry.FindFunctionTypeInfo(pgmBuilder.GetTypeEnvironment(), typeInfoHelper, nullptr,
  620. udfFuncName, userType, typeConfig, flags, {}, nullptr, &funcInfo);
  621. MKQL_ENSURE(status.IsOk(), status.GetError());
  622. auto callable = pgmBuilder.Udf(udfFuncName);
  623. return std::vector<TRuntimeNode>{callable, pgmBuilder.NewDataLiteral(testVal)};
  624. };
  625. Y_UNIT_TEST(TestVerifyArgsCallableCorrect) {
  626. ui32 testVal = 44;
  627. BuildArgsFunc argsFunc = [testVal](TProgramBuilder& pgmBuilder) {
  628. return MakeCallableInArgs(testVal, pgmBuilder);
  629. };
  630. ValidateValueFunc validateFunc = [testVal](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  631. Y_UNUSED(valueBuilder);
  632. UNIT_ASSERT_VALUES_EQUAL(testVal, value.Get<ui32>());
  633. };
  634. ProcessSimpleUdfFunc("UtUDF.BackSub2Mul2", argsFunc, validateFunc);
  635. }
  636. Y_UNIT_TEST(TestVerifyArgsCallableBrokenOnArgument) {
  637. ui32 testVal = 101;
  638. BuildArgsFunc argsFunc = [testVal](TProgramBuilder& pgmBuilder) {
  639. return MakeCallableInArgs(testVal, pgmBuilder);
  640. };
  641. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.BackSub2Mul2", argsFunc), TUdfValidateException);
  642. }
  643. Y_UNIT_TEST(TestVerifyArgsCallableBrokenOnReturn) {
  644. ui32 testVal = 1;
  645. BuildArgsFunc argsFunc = [testVal](TProgramBuilder& pgmBuilder) {
  646. return MakeCallableInArgs(testVal, pgmBuilder);
  647. };
  648. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.BackSub2Mul2", argsFunc), TUdfValidateException);
  649. }
  650. Y_UNIT_TEST(TestUdfResultCheckEmptySeqList) {
  651. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  652. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(0)};
  653. };
  654. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  655. Y_UNUSED(valueBuilder);
  656. auto listIter = value.GetListIterator();
  657. UNIT_ASSERT(!listIter.Skip());
  658. };
  659. ProcessSimpleUdfFunc("UtUDF.SeqList", argsFunc, validateFunc);
  660. }
  661. Y_UNIT_TEST(TestUdfResultCheckSeqList) {
  662. static constexpr ui32 listSize = 31;
  663. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  664. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(listSize)};
  665. };
  666. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  667. Y_UNUSED(valueBuilder);
  668. ui32 index = 0;
  669. auto listIter = value.GetListIterator();
  670. for (NUdf::TUnboxedValue item; listIter.Next(item); ++index) {
  671. UNIT_ASSERT_VALUES_EQUAL(item.Get<ui32>(), index);
  672. }
  673. UNIT_ASSERT_VALUES_EQUAL(index, listSize);
  674. };
  675. ProcessSimpleUdfFunc("UtUDF.SeqList", argsFunc, validateFunc);
  676. }
  677. Y_UNIT_TEST(TestUdfResultCheckSeqListWithHoleFirst) {
  678. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  679. const ui32 listSize = 31;
  680. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(listSize),
  681. pgmBuilder.NewDataLiteral<ui32>(0)};
  682. };
  683. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  684. Y_UNUSED(valueBuilder);
  685. auto listIter = value.GetListIterator();
  686. NUdf::TUnboxedValue item;
  687. UNIT_ASSERT_EXCEPTION(listIter.Next(item), TUdfValidateException);
  688. for (ui32 index = 1; listIter.Next(item); ++index) {
  689. UNIT_ASSERT_VALUES_EQUAL(item.Get<ui32>(), index);
  690. }
  691. };
  692. ProcessSimpleUdfFunc("UtUDF.SeqListWithHole", argsFunc, validateFunc);
  693. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.SeqListWithHole", argsFunc, validateFunc, {}, NUdf::EValidateMode::Greedy), TUdfValidateException);
  694. }
  695. Y_UNIT_TEST(TestUdfResultCheckSeqListWithHoleMiddle) {
  696. static constexpr ui32 listSize = 31;
  697. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  698. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(listSize),
  699. pgmBuilder.NewDataLiteral(listSize / 2)};
  700. };
  701. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  702. Y_UNUSED(valueBuilder);
  703. UNIT_ASSERT_VALUES_EQUAL(value.GetListLength(), listSize);
  704. ui32 index = 0;
  705. const auto listIter = value.GetListIterator();
  706. for (NUdf::TUnboxedValue item; index < listSize / 2 && listIter.Next(item); ++index) {
  707. UNIT_ASSERT_VALUES_EQUAL(item.Get<ui32>(), index);
  708. }
  709. NUdf::TUnboxedValue bad;
  710. UNIT_ASSERT_EXCEPTION(listIter.Next(bad), TUdfValidateException);
  711. ++index;
  712. for (NUdf::TUnboxedValue item; listIter.Next(item); ++index) {
  713. UNIT_ASSERT_VALUES_EQUAL(item.Get<ui32>(), index);
  714. }
  715. };
  716. ProcessSimpleUdfFunc("UtUDF.SeqListWithHole", argsFunc, validateFunc);
  717. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.SeqListWithHole", argsFunc, validateFunc, {}, NUdf::EValidateMode::Greedy), TUdfValidateException);
  718. }
  719. Y_UNIT_TEST(TestUdfResultCheckSeqListWithHoleLast) {
  720. static constexpr ui32 listSize = 31;
  721. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  722. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(listSize),
  723. pgmBuilder.NewDataLiteral(listSize - 1)};
  724. };
  725. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  726. Y_UNUSED(valueBuilder);
  727. UNIT_ASSERT_VALUES_EQUAL(value.GetListLength(), listSize);
  728. ui32 index = 0;
  729. auto listIter = value.GetListIterator();
  730. for (NUdf::TUnboxedValue item; index < listSize - 1 && listIter.Next(item); ++index) {
  731. UNIT_ASSERT_VALUES_EQUAL(item.Get<ui32>(), index);
  732. }
  733. UNIT_ASSERT_VALUES_EQUAL(index, listSize - 1);
  734. NUdf::TUnboxedValue bad;
  735. UNIT_ASSERT_EXCEPTION(listIter.Next(bad), TUdfValidateException);
  736. };
  737. ProcessSimpleUdfFunc("UtUDF.SeqListWithHole", argsFunc, validateFunc);
  738. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.SeqListWithHole", argsFunc, validateFunc, {}, NUdf::EValidateMode::Greedy), TUdfValidateException);
  739. }
  740. Y_UNIT_TEST(TestUdfResultCheckTuple) {
  741. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  742. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  743. };
  744. ProcessSimpleUdfFunc("UtUDF.Tuple", argsFunc);
  745. }
  746. Y_UNIT_TEST(TestUdfResultCheckTupleWithHoleFirst) {
  747. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  748. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(0)};
  749. };
  750. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.Tuple", argsFunc), TUdfValidateException);
  751. }
  752. Y_UNIT_TEST(TestUdfResultCheckTupleWithHoleMiddle) {
  753. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  754. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(std::tuple_size<decltype(TUPLE)>::value / 2)};
  755. };
  756. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.Tuple", argsFunc), TUdfValidateException);
  757. }
  758. Y_UNIT_TEST(TestUdfResultCheckTupleWithHoleLast) {
  759. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  760. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(std::tuple_size<decltype(TUPLE)>::value - 1)};
  761. };
  762. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.Tuple", argsFunc), TUdfValidateException);
  763. }
  764. Y_UNIT_TEST(TestUdfResultCheckDictDigitDigitFull) {
  765. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  766. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE),
  767. pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  768. };
  769. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  770. Y_UNUSED(valueBuilder);
  771. auto dictIter = value.GetDictIterator();
  772. ui32 index = 0;
  773. for (NUdf::TUnboxedValue key, payload; dictIter.NextPair(key, payload); ++index) {
  774. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui32>(), DICT_DIGIT2DIGIT[index].first);
  775. UNIT_ASSERT_VALUES_EQUAL(payload.Get<ui64>(), DICT_DIGIT2DIGIT[index].second);
  776. }
  777. UNIT_ASSERT_VALUES_EQUAL(index, DICT_DIGIT2DIGIT.size());
  778. };
  779. ProcessSimpleUdfFunc("UtUDF.DictDigDig", argsFunc, validateFunc);
  780. }
  781. Y_UNIT_TEST(TestUdfResultCheckDictDigitDigitKeyHole) {
  782. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  783. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(0),
  784. pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  785. };
  786. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  787. Y_UNUSED(valueBuilder);
  788. auto dictIter = value.GetDictIterator();
  789. NUdf::TUnboxedValue key, payload;
  790. UNIT_ASSERT_EXCEPTION(dictIter.NextPair(key, payload), TUdfValidateException);
  791. for (ui32 index = 1; dictIter.NextPair(key, payload); ++index) {
  792. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui32>(), DICT_DIGIT2DIGIT[index].first);
  793. UNIT_ASSERT_VALUES_EQUAL(payload.Get<ui64>(), DICT_DIGIT2DIGIT[index].second);
  794. }
  795. };
  796. ProcessSimpleUdfFunc("UtUDF.DictDigDig", argsFunc, validateFunc);
  797. }
  798. Y_UNIT_TEST(TestUdfResultCheckDictDigitDigitValueHole) {
  799. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  800. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE),
  801. pgmBuilder.NewDataLiteral<ui32>(DICT_DIGIT2DIGIT.size() - 1)};
  802. };
  803. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  804. Y_UNUSED(valueBuilder);
  805. auto dictIter = value.GetDictIterator();
  806. NUdf::TUnboxedValue key, payload;
  807. for (ui32 index = 0; index < DICT_DIGIT2DIGIT.size() - 1 && dictIter.NextPair(key, payload); ++index) {
  808. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui32>(), DICT_DIGIT2DIGIT[index].first);
  809. UNIT_ASSERT_VALUES_EQUAL(payload.Get<ui64>(), DICT_DIGIT2DIGIT[index].second);
  810. }
  811. UNIT_ASSERT_EXCEPTION(dictIter.NextPair(key, payload), TUdfValidateException);
  812. };
  813. ProcessSimpleUdfFunc("UtUDF.DictDigDig", argsFunc, validateFunc);
  814. }
  815. Y_UNIT_TEST(TestUdfResultCheckDictDigitDigitHoleAsOptKeyHole) {
  816. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  817. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE),
  818. pgmBuilder.NewDataLiteral<ui32>(0)};
  819. };
  820. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  821. Y_UNUSED(valueBuilder);
  822. auto dictIter = value.GetDictIterator();
  823. NUdf::TUnboxedValue key, payload;
  824. UNIT_ASSERT_EXCEPTION(dictIter.NextPair(key, payload), TUdfValidateException);
  825. for (ui32 index = 1; dictIter.NextPair(key, payload); ++index) {
  826. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui32>(), DICT_DIGIT2DIGIT[index].first);
  827. UNIT_ASSERT_VALUES_EQUAL(payload.Get<ui64>(), DICT_DIGIT2DIGIT[index].second);
  828. }
  829. };
  830. ProcessSimpleUdfFunc("UtUDF.DictDigDig", argsFunc, validateFunc);
  831. }
  832. Y_UNIT_TEST(TestUdfResultCheckDictDigitDigitHoleAsOptValueHole) {
  833. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  834. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(DICT_DIGIT2DIGIT.size() - 1),
  835. pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  836. };
  837. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  838. Y_UNUSED(valueBuilder);
  839. auto dictIter = value.GetDictIterator();
  840. NUdf::TUnboxedValue key, payload;
  841. for (ui32 index = 0; index < DICT_DIGIT2DIGIT.size() - 1 && dictIter.NextPair(key, payload); ++index) {
  842. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui32>(), DICT_DIGIT2DIGIT[index].first);
  843. UNIT_ASSERT_VALUES_EQUAL(payload.Get<ui64>(), DICT_DIGIT2DIGIT[index].second);
  844. }
  845. UNIT_ASSERT_EXCEPTION(dictIter.NextPair(key, payload), TUdfValidateException);
  846. };
  847. ProcessSimpleUdfFunc("UtUDF.DictDigDig", argsFunc, validateFunc);
  848. }
  849. Y_UNIT_TEST(TestUdfResultCheckPersonStruct) {
  850. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  851. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  852. };
  853. ProcessSimpleUdfFunc("UtUDF.PersonStruct", argsFunc);
  854. }
  855. Y_UNIT_TEST(TestUdfResultCheckPersonStructWithHoleFirst) {
  856. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  857. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(0)};
  858. };
  859. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.PersonStruct", argsFunc), TUdfValidateException);
  860. }
  861. Y_UNIT_TEST(TestUdfResultCheckPersonStructWithHoleMiddle) {
  862. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  863. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(1)};
  864. };
  865. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.PersonStruct", argsFunc), TUdfValidateException);
  866. }
  867. Y_UNIT_TEST(TestUdfResultCheckPersonStructWithHoleLast) {
  868. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  869. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral<ui32>(2)};
  870. };
  871. UNIT_ASSERT_EXCEPTION(ProcessSimpleUdfFunc("UtUDF.PersonStruct", argsFunc), TUdfValidateException);
  872. }
  873. Y_UNIT_TEST(TestUdfResultCheckTupleOfPersonStruct) {
  874. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  875. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  876. };
  877. FullValidateValueFunc fullValidateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder,
  878. const TType* type) {
  879. bool wrapped = false;
  880. TValidate<TValidateErrorPolicyThrow, TValidateModeGreedy<TValidateErrorPolicyThrow>>::Value(valueBuilder, type, NUdf::TUnboxedValuePod(value), "full verify func", &wrapped);
  881. UNIT_ASSERT(!wrapped);
  882. TValidate<TValidateErrorPolicyThrow, TValidateModeLazy<TValidateErrorPolicyThrow>>::Value(valueBuilder, type, NUdf::TUnboxedValuePod(value), "full verify func", &wrapped);
  883. UNIT_ASSERT(wrapped);
  884. };
  885. ProcessSimpleUdfFunc("UtUDF.TupleOfPersonStruct", argsFunc, {}, fullValidateFunc);
  886. ProcessSimpleUdfFunc("UtUDF.TupleOfPersonStruct", argsFunc, {}, fullValidateFunc, NUdf::EValidateMode::Greedy);
  887. }
  888. Y_UNIT_TEST(TestUdfResultCheckTupleOfPersonStructNoList) {
  889. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  890. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  891. };
  892. FullValidateValueFunc fullValidateFunc = [](const NUdf::TUnboxedValuePod& value,
  893. const NUdf::IValueBuilder* valueBuilder, const TType* type) {
  894. bool wrapped = false;
  895. TValidate<TValidateErrorPolicyThrow>::Value(valueBuilder, type, NUdf::TUnboxedValuePod(value), "full verify func", &wrapped);
  896. UNIT_ASSERT(!wrapped);
  897. };
  898. ProcessSimpleUdfFunc("UtUDF.TupleOfPersonStructNoList", argsFunc, {}, fullValidateFunc);
  899. ProcessSimpleUdfFunc("UtUDF.TupleOfPersonStructNoList", argsFunc, {}, fullValidateFunc, NUdf::EValidateMode::Greedy);
  900. }
  901. void ValidateDictOfPersonStructFunc(const NUdf::TUnboxedValuePod& value, ui32 lookupIndex = 2, ui32 broken_index = RAW_INDEX_NO_HOLE) {
  902. const auto person = value.Lookup(NUdf::TUnboxedValuePod(ui64(lookupIndex)));
  903. UNIT_ASSERT(person);
  904. auto firstName = person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[0]);
  905. UNIT_ASSERT_VALUES_EQUAL(TString(firstName.AsStringRef()), LIST_OF_STRUCT_PERSON[lookupIndex].FirstName);
  906. auto lastName = person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[1]);
  907. UNIT_ASSERT_VALUES_EQUAL(TString(lastName.AsStringRef()), LIST_OF_STRUCT_PERSON[lookupIndex].LastName);
  908. UNIT_ASSERT_VALUES_EQUAL(person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[2]).Get<ui32>(), LIST_OF_STRUCT_PERSON[lookupIndex].Age);
  909. UNIT_ASSERT(!person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[3]));
  910. auto dictIter = value.GetDictIterator();
  911. NUdf::TUnboxedValue key, payload;
  912. for (ui32 index = 0; index < broken_index && dictIter.NextPair(key, payload); ++index) {
  913. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui64>(), index);
  914. auto person = payload;
  915. auto firstName = person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[0]);
  916. UNIT_ASSERT_VALUES_EQUAL(TString(firstName.AsStringRef()), LIST_OF_STRUCT_PERSON[index].FirstName);
  917. auto lastName = person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[1]);
  918. UNIT_ASSERT_VALUES_EQUAL(TString(lastName.AsStringRef()), LIST_OF_STRUCT_PERSON[index].LastName);
  919. UNIT_ASSERT_VALUES_EQUAL(person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[2]).Get<ui32>(), LIST_OF_STRUCT_PERSON[index].Age);
  920. const auto origListPtr = LIST_OF_STRUCT_PERSON[index].Tags;
  921. if (origListPtr.empty()) {
  922. UNIT_ASSERT(!person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[3]));
  923. } else {
  924. auto memberTags = person.GetElement(NUdf::PersonStructWithOptList::MetaIndexes[3]);
  925. UNIT_ASSERT(memberTags);
  926. UNIT_ASSERT_VALUES_EQUAL(memberTags.GetListLength(), origListPtr.size());
  927. auto origIter = origListPtr.begin();
  928. auto iter = memberTags.GetListIterator();
  929. for (NUdf::TUnboxedValue item; iter.Next(item); ++origIter) {
  930. UNIT_ASSERT_VALUES_EQUAL(item.Get<ui32>(), *origIter);
  931. }
  932. }
  933. }
  934. if (broken_index < RAW_INDEX_NO_HOLE)
  935. UNIT_ASSERT_EXCEPTION(dictIter.NextPair(key, payload), TUdfValidateException);
  936. }
  937. Y_UNIT_TEST(TestUdfResultCheckListOfPersonStructToIndexDict) {
  938. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  939. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  940. };
  941. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder) {
  942. Y_UNUSED(valueBuilder);
  943. ValidateDictOfPersonStructFunc(value);
  944. };
  945. ProcessSimpleUdfFunc("UtUDF.ListOfPersonStructToIndexDict", argsFunc, validateFunc);
  946. }
  947. Y_UNIT_TEST(TestUdfResultCheckListOfPersonStruct) {
  948. BuildArgsFunc argsFunc = [](TProgramBuilder& pgmBuilder) {
  949. return std::vector<TRuntimeNode>{pgmBuilder.NewDataLiteral(RAW_INDEX_NO_HOLE)};
  950. };
  951. FullValidateValueFunc fullValidateFunc = [](const NUdf::TUnboxedValuePod& value,
  952. const NUdf::IValueBuilder* valueBuilder, const TType* type) {
  953. Y_UNUSED(type);
  954. auto indexDict = valueBuilder->ToIndexDict(value);
  955. ValidateDictOfPersonStructFunc(indexDict);
  956. };
  957. ProcessSimpleUdfFunc("UtUDF.ListOfPersonStruct", argsFunc, {}, fullValidateFunc);
  958. }
  959. Y_UNIT_TEST(TestUdfResultCheckListOfPersonStructWithBrokenIndexToDict) {
  960. FullValidateValueFunc fullValidateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder* valueBuilder,
  961. const TType* type) {
  962. Y_UNUSED(type);
  963. auto indexDict = valueBuilder->ToIndexDict(value);
  964. static_assert(RAW_BROKEN_INDEX_LIST_TO_DICT == 1, "a list is too small");
  965. ValidateDictOfPersonStructFunc(indexDict, RAW_BROKEN_INDEX_LIST_TO_DICT - 1, RAW_BROKEN_INDEX_LIST_TO_DICT);
  966. /// verify lookup fail on broken index
  967. UNIT_ASSERT_EXCEPTION(ValidateDictOfPersonStructFunc(indexDict, RAW_BROKEN_INDEX_LIST_TO_DICT, RAW_BROKEN_INDEX_LIST_TO_DICT), TUdfValidateException);
  968. ValidateDictOfPersonStructFunc(indexDict, RAW_BROKEN_INDEX_LIST_TO_DICT + 1, RAW_BROKEN_INDEX_LIST_TO_DICT);
  969. };
  970. ProcessSimpleUdfFunc("UtUDF.ListOfPersonStructWithBrokenIndexToDict", {}, {}, fullValidateFunc);
  971. }
  972. Y_UNIT_TEST(TestUdfResultCheckDictOfPerson) {
  973. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder*) {
  974. auto dictIter = value.GetDictIterator();
  975. NUdf::TUnboxedValue key, payload;
  976. for (ui32 index = 0; dictIter.NextPair(key, payload); ++index) {
  977. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui32>(), MAKE_DICT_DIGIT2PERSON()[index].first);
  978. auto person = payload;
  979. auto firstName = person.GetElement(NUdf::PersonStruct::MetaIndexes[0]);
  980. UNIT_ASSERT_VALUES_EQUAL(TString(firstName.AsStringRef()), DICT_DIGIT2PERSON_BROKEN_CONTENT_BY_INDEX[index]->FirstName);
  981. auto lastName = person.GetElement(NUdf::PersonStruct::MetaIndexes[1]);
  982. UNIT_ASSERT_VALUES_EQUAL(TString(lastName.AsStringRef()), DICT_DIGIT2PERSON_BROKEN_CONTENT_BY_INDEX[index]->LastName);
  983. UNIT_ASSERT_VALUES_EQUAL(person.GetElement(NUdf::PersonStruct::MetaIndexes[2]).Get<ui32>(), DICT_DIGIT2PERSON_BROKEN_CONTENT_BY_INDEX[index]->Age);
  984. }
  985. };
  986. ProcessSimpleUdfFunc("UtUDF.DictOfPerson", {}, validateFunc);
  987. }
  988. Y_UNIT_TEST(TestUdfResultCheckDictOfPersonBroken) {
  989. ValidateValueFunc validateFunc = [](const NUdf::TUnboxedValuePod& value, const NUdf::IValueBuilder*) {
  990. auto dictIter = value.GetDictIterator();
  991. NUdf::TUnboxedValue key, payload;
  992. for (ui32 index = 0; index < DICT_DIGIT2PERSON_BROKEN_PERSON_INDEX && dictIter.NextPair(key, payload); ++index) {
  993. UNIT_ASSERT_VALUES_EQUAL(key.Get<ui32>(), MAKE_DICT_DIGIT2PERSON_BROKEN()[index].first);
  994. auto person = payload;
  995. auto firstName = person.GetElement(NUdf::PersonStruct::MetaIndexes[0]);
  996. UNIT_ASSERT_VALUES_EQUAL(TString(firstName.AsStringRef()), DICT_DIGIT2PERSON_BROKEN_CONTENT_BY_INDEX[index]->FirstName);
  997. auto lastName = person.GetElement(NUdf::PersonStruct::MetaIndexes[1]);
  998. UNIT_ASSERT_VALUES_EQUAL(TString(lastName.AsStringRef()), DICT_DIGIT2PERSON_BROKEN_CONTENT_BY_INDEX[index]->LastName);
  999. UNIT_ASSERT_VALUES_EQUAL(person.GetElement(NUdf::PersonStruct::MetaIndexes[2]).Get<ui32>(), DICT_DIGIT2PERSON_BROKEN_CONTENT_BY_INDEX[index]->Age);
  1000. }
  1001. UNIT_ASSERT_EXCEPTION(dictIter.NextPair(key, payload), TUdfValidateException);
  1002. };
  1003. ProcessSimpleUdfFunc("UtUDF.DictOfPersonBroken", {}, validateFunc);
  1004. }
  1005. }
  1006. }