yql_opt_range.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. #include "yql_opt_range.h"
  2. #include "yql_opt_utils.h"
  3. #include "yql_expr_type_annotation.h"
  4. #include <yql/essentials/public/udf/tz/udf_tz.h>
  5. namespace NYql {
  6. namespace {
  7. struct TRangeBoundary {
  8. TExprNode::TPtr Value; // top level null means infinity
  9. bool Included = false;
  10. };
  11. TExprNode::TPtr BuildBoundaryNode(TPositionHandle pos, const TRangeBoundary& boundary, TExprContext& ctx) {
  12. YQL_ENSURE(boundary.Value);
  13. return ctx.Builder(pos)
  14. .List()
  15. .Add(0, boundary.Value)
  16. .Callable(1, "Int32")
  17. .Atom(0, boundary.Included ? "1" : "0", TNodeFlags::Default)
  18. .Seal()
  19. .Seal()
  20. .Build();
  21. }
  22. TExprNode::TPtr BuildRange(TPositionHandle pos, const TRangeBoundary& left, const TRangeBoundary& right,
  23. TExprContext& ctx)
  24. {
  25. return ctx.NewList(pos, { BuildBoundaryNode(pos, left, ctx), BuildBoundaryNode(pos, right, ctx) });
  26. }
  27. TExprNode::TPtr BuildRangeSingle(TPositionHandle pos, const TRangeBoundary& left, const TRangeBoundary& right,
  28. TExprContext& ctx)
  29. {
  30. return ctx.NewCallable(pos, "AsRange", { BuildRange(pos, left, right, ctx) });
  31. }
  32. TMaybe<EDataSlot> GetBaseDataSlot(const TTypeAnnotationNode* keyType) {
  33. auto baseType = RemoveAllOptionals(keyType);
  34. TMaybe<EDataSlot> result;
  35. if (baseType->GetKind() == ETypeAnnotationKind::Data) {
  36. result = baseType->Cast<TDataExprType>()->GetSlot();
  37. }
  38. return result;
  39. }
  40. bool HasUncomparableNaNs(const TTypeAnnotationNode* keyType) {
  41. // PostgreSQL comparison operators treat NaNs as biggest flating values
  42. // this behavior matches ordering in ORDER BY, so we don't need special NaN handling for Pg types
  43. auto slot = GetBaseDataSlot(keyType);
  44. return slot && (NUdf::GetDataTypeInfo(*slot).Features & (NUdf::EDataTypeFeatures::FloatType | NUdf::EDataTypeFeatures::DecimalType));
  45. }
  46. TExprNode::TPtr MakeNaNBoundary(TPositionHandle pos, const TTypeAnnotationNode* keyType, TExprContext& ctx) {
  47. YQL_ENSURE(HasUncomparableNaNs(keyType));
  48. auto baseType = RemoveAllOptionals(keyType);
  49. auto keySlot = baseType->Cast<TDataExprType>()->GetSlot();
  50. return ctx.Builder(pos)
  51. .Callable("SafeCast")
  52. .Callable(0, NUdf::GetDataTypeInfo(keySlot).Name)
  53. .Do([&](TExprNodeBuilder& parent) -> TExprNodeBuilder& {
  54. parent.Atom(0, "nan", TNodeFlags::Default);
  55. if (keySlot == EDataSlot::Decimal) {
  56. auto decimalType = baseType->Cast<TDataExprParamsType>();
  57. parent.Atom(1, decimalType->GetParamOne());
  58. parent.Atom(2, decimalType->GetParamTwo());
  59. }
  60. return parent;
  61. })
  62. .Seal()
  63. .Add(1, ExpandType(pos, *ctx.MakeType<TOptionalExprType>(keyType), ctx))
  64. .Seal()
  65. .Build();
  66. }
  67. TExprNode::TPtr TzRound(const TExprNode::TPtr& key, const TTypeAnnotationNode* keyType, bool down, TExprContext& ctx) {
  68. TPositionHandle pos = key->Pos();
  69. const auto& timeZones = NUdf::GetTimezones();
  70. const size_t tzSize = timeZones.size();
  71. YQL_ENSURE(tzSize > 0);
  72. size_t targetTzId = 0;
  73. if (!down) {
  74. for (targetTzId = tzSize - 1; targetTzId > 0; --targetTzId) {
  75. if (!timeZones[targetTzId].empty()) {
  76. break;
  77. }
  78. }
  79. }
  80. YQL_ENSURE(!timeZones[targetTzId].empty());
  81. return ctx.Builder(pos)
  82. .Callable("Coalesce")
  83. .Callable(0, "AddTimezone")
  84. .Callable(0, "RemoveTimezone")
  85. .Callable(0, "SafeCast")
  86. .Add(0, key)
  87. .Add(1, ExpandType(pos, *RemoveAllOptionals(keyType), ctx))
  88. .Seal()
  89. .Seal()
  90. .Callable(1, "Uint16")
  91. .Atom(0, ToString(targetTzId), TNodeFlags::Default)
  92. .Seal()
  93. .Seal()
  94. .Add(1, key)
  95. .Seal()
  96. .Build();
  97. }
  98. TRangeBoundary BuildPlusInf(TPositionHandle pos, const TTypeAnnotationNode* keyType, TExprContext& ctx, bool excludeNaN = true) {
  99. TRangeBoundary result;
  100. if (excludeNaN && HasUncomparableNaNs(keyType)) {
  101. // exclude NaN value (NaN is ordered as largest floating point value)
  102. result.Value = MakeNaNBoundary(pos, keyType, ctx);
  103. } else {
  104. auto optKeyTypeNode = ExpandType(pos, *ctx.MakeType<TOptionalExprType>(keyType), ctx);
  105. result.Value = ctx.NewCallable(pos, "Nothing", { optKeyTypeNode });
  106. }
  107. result.Included = false;
  108. return result;
  109. }
  110. TRangeBoundary BuildMinusInf(TPositionHandle pos, const TTypeAnnotationNode* keyType, TExprContext& ctx) {
  111. // start from first non-null value
  112. auto optKeyTypeNode = ExpandType(pos, *ctx.MakeType<TOptionalExprType>(keyType), ctx);
  113. auto optBaseKeyTypeNode = ExpandType(pos, *ctx.MakeType<TOptionalExprType>(RemoveAllOptionals(keyType)), ctx);
  114. TExprNode::TPtr largestNull;
  115. if (keyType->GetKind() == ETypeAnnotationKind::Pg) {
  116. largestNull = ctx.Builder(pos)
  117. .Callable("Just")
  118. .Callable(0, "Nothing")
  119. .Add(0, ExpandType(pos, *keyType, ctx))
  120. .Seal()
  121. .Seal()
  122. .Build();
  123. } else {
  124. largestNull = ctx.Builder(pos)
  125. .Callable("SafeCast")
  126. .Callable(0, "Nothing")
  127. .Add(0, optBaseKeyTypeNode)
  128. .Seal()
  129. .Add(1, optKeyTypeNode)
  130. .Seal()
  131. .Build();
  132. }
  133. TRangeBoundary result;
  134. result.Value = largestNull;
  135. result.Included = false;
  136. return result;
  137. }
  138. TExprNode::TPtr MakeWidePointRangeLambda(TPositionHandle pos, const TTypeAnnotationNode* keyType, TExprContext& ctx) {
  139. TStringBuf name;
  140. TString maxValueStr;
  141. const TTypeAnnotationNode* baseKeyType = RemoveAllOptionals(keyType);
  142. const auto keySlot = baseKeyType->Cast<TDataExprType>()->GetSlot();
  143. switch (keySlot) {
  144. case EDataSlot::Int8: name = "Int8"; maxValueStr = ToString(Max<i8>()); break;
  145. case EDataSlot::Uint8: name = "Uint8"; maxValueStr = ToString(Max<ui8>()); break;
  146. case EDataSlot::Int16: name = "Int16"; maxValueStr = ToString(Max<i16>()); break;
  147. case EDataSlot::Uint16: name = "Uint16"; maxValueStr = ToString(Max<ui16>()); break;
  148. case EDataSlot::Int32: name = "Int32"; maxValueStr = ToString(Max<i32>()); break;
  149. case EDataSlot::Uint32: name = "Uint32"; maxValueStr = ToString(Max<ui32>()); break;
  150. case EDataSlot::Int64: name = "Int64"; maxValueStr = ToString(Max<i64>()); break;
  151. case EDataSlot::Uint64: name = "Uint64"; maxValueStr = ToString(Max<ui64>()); break;
  152. case EDataSlot::Date: name = "Date"; maxValueStr = ToString(NUdf::MAX_DATE - 1); break;
  153. case EDataSlot::Datetime: name = "Datetime"; maxValueStr = ToString(NUdf::MAX_DATETIME - 1); break;
  154. case EDataSlot::Timestamp: name = "Timestamp"; maxValueStr = ToString(NUdf::MAX_TIMESTAMP - 1); break;
  155. case EDataSlot::Date32: name = "Date32"; maxValueStr = ToString(NUdf::MAX_DATE32); break;
  156. case EDataSlot::Datetime64: name = "Datetime64"; maxValueStr = ToString(NUdf::MAX_DATETIME64); break;
  157. case EDataSlot::Timestamp64: name = "Timestamp64"; maxValueStr = ToString(NUdf::MAX_TIMESTAMP64); break;
  158. default:
  159. ythrow yexception() << "Unexpected type: " << baseKeyType->Cast<TDataExprType>()->GetName();
  160. }
  161. TExprNode::TPtr maxValue = ctx.NewCallable(pos, name, { ctx.NewAtom(pos, maxValueStr, TNodeFlags::Default) });
  162. TExprNode::TPtr addValue;
  163. if (keySlot == EDataSlot::Date) {
  164. addValue = ctx.NewCallable(pos, "Interval", { ctx.NewAtom(pos, "86400000000", TNodeFlags::Default) });
  165. } else if (keySlot == EDataSlot::Datetime) {
  166. addValue = ctx.NewCallable(pos, "Interval", { ctx.NewAtom(pos, "1000000", TNodeFlags::Default) });
  167. } else if (keySlot == EDataSlot::Timestamp) {
  168. addValue = ctx.NewCallable(pos, "Interval", { ctx.NewAtom(pos, "1", TNodeFlags::Default) });
  169. } else if (keySlot == EDataSlot::Date32) {
  170. addValue = ctx.NewCallable(pos, "Interval64", { ctx.NewAtom(pos, "86400000000", TNodeFlags::Default) });
  171. } else if (keySlot == EDataSlot::Datetime64) {
  172. addValue = ctx.NewCallable(pos, "Interval64", { ctx.NewAtom(pos, "1000000", TNodeFlags::Default) });
  173. } else if (keySlot == EDataSlot::Timestamp64) {
  174. addValue = ctx.NewCallable(pos, "Interval64", { ctx.NewAtom(pos, "1", TNodeFlags::Default) });
  175. } else {
  176. addValue = ctx.NewCallable(pos, name, { ctx.NewAtom(pos, "1", TNodeFlags::Default) });
  177. }
  178. TExprNode::TPtr key = ctx.NewArgument(pos, "optKey");
  179. TExprNode::TPtr castedKey = ctx.Builder(pos)
  180. .Callable("SafeCast")
  181. .Add(0, key)
  182. .Add(1, ExpandType(pos, *ctx.MakeType<TOptionalExprType>(baseKeyType), ctx))
  183. .Seal()
  184. .Build();
  185. TExprNode::TPtr leftBoundary = ctx.Builder(pos)
  186. .List()
  187. .Add(0, key)
  188. .Callable(1, "Int32")
  189. .Atom(0, "1", TNodeFlags::Default)
  190. .Seal()
  191. .Seal()
  192. .Build();
  193. auto body = ctx.Builder(pos)
  194. .Callable("AsRange")
  195. .List(0)
  196. .Add(0, leftBoundary)
  197. .Callable(1, "If")
  198. .Callable(0, "Coalesce")
  199. .Callable(0, "==")
  200. .Add(0, key)
  201. .Add(1, maxValue)
  202. .Seal()
  203. .Callable(1, "Bool")
  204. .Atom(0, "true", TNodeFlags::Default)
  205. .Seal()
  206. .Seal()
  207. .Add(1, leftBoundary)
  208. .List(2)
  209. .Callable(0, "SafeCast")
  210. .Callable(0, "+")
  211. .Add(0, castedKey)
  212. .Add(1, addValue)
  213. .Seal()
  214. .Add(1, ExpandType(pos, *keyType, ctx))
  215. .Seal()
  216. .Callable(1, "Int32")
  217. .Atom(0, "0", TNodeFlags::Default)
  218. .Seal()
  219. .Seal()
  220. .Seal()
  221. .Seal()
  222. .Seal()
  223. .Build();
  224. return ctx.NewLambda(pos, ctx.NewArguments(pos, { key }), std::move(body));
  225. }
  226. TExprNode::TPtr BuildNormalRangeLambdaRaw(TPositionHandle pos, const TTypeAnnotationNode* keyType,
  227. TStringBuf op, TExprContext& ctx)
  228. {
  229. // key is argument of Optional<keyType> type
  230. const auto key = ctx.NewArgument(pos, "key");
  231. const auto keySlot = GetBaseDataSlot(keyType);
  232. const bool isTzKey = keySlot && (NUdf::GetDataTypeInfo(*keySlot).Features & NUdf::EDataTypeFeatures::TzDateType);
  233. const bool isIntegralOrDateKey = keySlot && (NUdf::GetDataTypeInfo(*keySlot).Features &
  234. (NUdf::EDataTypeFeatures::IntegralType | NUdf::EDataTypeFeatures::DateType));
  235. const auto downKey = isTzKey ? TzRound(key, keyType, true, ctx) : key;
  236. const auto upKey = isTzKey ? TzRound(key, keyType, false, ctx) : key;
  237. if (op == "!=") {
  238. TRangeBoundary left, right;
  239. left = BuildMinusInf(pos, keyType, ctx);
  240. right.Value = downKey;
  241. right.Included = false;
  242. auto leftRange = BuildRange(pos, left, right, ctx);
  243. left.Value = upKey;
  244. left.Included = false;
  245. bool excludeNaN = false;
  246. right = BuildPlusInf(pos, keyType, ctx, excludeNaN);
  247. auto rightRange = BuildRange(pos, left, right, ctx);
  248. auto body = ctx.NewCallable(pos, "AsRange", { leftRange, rightRange });
  249. if (HasUncomparableNaNs(keyType)) {
  250. // NaN is not equal to any value (including NaN itself), so we must include it
  251. left.Included = true;
  252. left.Value = MakeNaNBoundary(pos, keyType, ctx);
  253. right = left;
  254. auto nan = BuildRangeSingle(pos, left, right, ctx);
  255. body = ctx.NewCallable(pos, "RangeUnion", { body, nan });
  256. }
  257. return ctx.NewLambda(pos, ctx.NewArguments(pos, { key }), std::move(body));
  258. }
  259. if (op == "===") {
  260. if (isIntegralOrDateKey) {
  261. return MakeWidePointRangeLambda(pos, keyType, ctx);
  262. }
  263. op = "==";
  264. }
  265. TRangeBoundary left, right;
  266. if (op == "==") {
  267. left.Value = downKey;
  268. right.Value = upKey;
  269. left.Included = right.Included = true;
  270. } else if (op == "<" || op == "<=") {
  271. left = BuildMinusInf(pos, keyType, ctx);
  272. right.Value = (op == "<=") ? upKey : downKey;
  273. right.Included = (op == "<=");
  274. } else if (op == ">" || op == ">=") {
  275. right = BuildPlusInf(pos, keyType, ctx);
  276. left.Value = (op == ">=") ? downKey : upKey;
  277. left.Included = (op == ">=");
  278. } else if (op == "Exists" || op == "NotExists" ) {
  279. YQL_ENSURE(keyType->GetKind() == ETypeAnnotationKind::Optional || keyType->GetKind() == ETypeAnnotationKind::Pg);
  280. auto nullKey = ctx.Builder(pos)
  281. .Callable("Just")
  282. .Callable(0, "Nothing")
  283. .Add(0, ExpandType(pos, *keyType, ctx))
  284. .Seal()
  285. .Seal()
  286. .Build();
  287. if (op == "NotExists") {
  288. left.Value = right.Value = nullKey;
  289. left.Included = right.Included = true;
  290. } else {
  291. left.Value = nullKey;
  292. left.Included = false;
  293. // Exists should include NaN
  294. bool excludeNaN = false;
  295. right = BuildPlusInf(pos, keyType, ctx, excludeNaN);
  296. }
  297. } else {
  298. YQL_ENSURE(false, "Unknown operation: " << op);
  299. }
  300. TExprNode::TPtr body = BuildRangeSingle(pos, left, right, ctx);
  301. if (op == "==" && HasUncomparableNaNs(keyType)) {
  302. auto fullRangeWithoutNaNs = BuildRangeSingle(pos,
  303. BuildMinusInf(pos, keyType, ctx),
  304. BuildPlusInf(pos, keyType, ctx), ctx);
  305. body = ctx.NewCallable(pos, "RangeIntersect", { body, fullRangeWithoutNaNs });
  306. }
  307. return ctx.NewLambda(pos, ctx.NewArguments(pos, { key }), std::move(body));
  308. }
  309. } //namespace
  310. TExprNode::TPtr ExpandRangeEmpty(const TExprNode::TPtr& node, TExprContext& ctx) {
  311. YQL_ENSURE(node->IsCallable("RangeEmpty"));
  312. if (node->ChildrenSize() == 0) {
  313. return ctx.RenameNode(*node, "EmptyList");
  314. }
  315. return ctx.NewCallable(node->Pos(), "List", { ExpandType(node->Pos(), *node->GetTypeAnn(), ctx) });
  316. }
  317. TExprNode::TPtr ExpandAsRange(const TExprNode::TPtr& node, TExprContext& ctx) {
  318. YQL_ENSURE(node->IsCallable("AsRange"));
  319. YQL_ENSURE(node->ChildrenSize());
  320. return ctx.NewCallable(node->Pos(), "RangeCreate", { ctx.RenameNode(*node, "AsList") });
  321. }
  322. TExprNode::TPtr ExpandRangeFor(const TExprNode::TPtr& node, TExprContext& ctx) {
  323. TPositionHandle pos = node->Pos();
  324. TStringBuf op = node->Head().Content();
  325. auto value = node->ChildPtr(1);
  326. const TTypeAnnotationNode* valueType = value->GetTypeAnn();
  327. const TTypeAnnotationNode* keyType = node->Tail().GetTypeAnn()->Cast<TTypeExprType>()->GetType();
  328. const TTypeAnnotationNode* optKeyType = ctx.MakeType<TOptionalExprType>(keyType);
  329. const TTypeAnnotationNode* keyBaseType = RemoveAllOptionals(keyType);
  330. const TTypeAnnotationNode* valueBaseType = RemoveAllOptionals(valueType);
  331. const TExprNode::TPtr castedToKey = ctx.NewCallable(pos, "StrictCast",
  332. { value, ExpandType(pos, *optKeyType, ctx) });
  333. const TExprNode::TPtr emptyRange = ctx.NewCallable(pos, "RangeEmpty",
  334. { ExpandType(pos, *keyType, ctx) });
  335. TExprNode::TPtr rangeForNullCast;
  336. if (op == "==" || op == "===" || op == "StartsWith") {
  337. rangeForNullCast = emptyRange;
  338. } else if (op == "!=" || op == "NotStartsWith") {
  339. bool excludeNaN = false;
  340. rangeForNullCast = BuildRangeSingle(pos,
  341. BuildMinusInf(pos, keyType, ctx),
  342. BuildPlusInf(pos, keyType, ctx, excludeNaN), ctx);
  343. }
  344. TExprNode::TPtr result;
  345. if (op == "Exists" || op == "NotExists") {
  346. result = ctx.Builder(pos)
  347. .Apply(BuildNormalRangeLambdaRaw(pos, keyType, op, ctx))
  348. .With(0, value) // value is not actually used for Exists/NotExists
  349. .Seal()
  350. .Build();
  351. } else if (op == "==" || op == "!=" || op == "===") {
  352. YQL_ENSURE(rangeForNullCast);
  353. result = ctx.Builder(pos)
  354. .Callable("If")
  355. .Callable(0, "HasNull")
  356. .Add(0, castedToKey)
  357. .Seal()
  358. .Add(1, rangeForNullCast)
  359. .Apply(2, BuildNormalRangeLambdaRaw(pos, keyType, op, ctx))
  360. .With(0, castedToKey)
  361. .Seal()
  362. .Seal()
  363. .Build();
  364. } else if (op == "StartsWith" || op == "NotStartsWith") {
  365. YQL_ENSURE(rangeForNullCast);
  366. const auto boundary = ctx.NewArgument(pos, "boundary");
  367. const auto next = ctx.NewCallable(pos, "NextValue", { boundary });
  368. TExprNode::TPtr rangeForIfNext;
  369. TExprNode::TPtr rangeForIfNotNext;
  370. if (op == "StartsWith") {
  371. const auto geBoundary = ctx.Builder(pos)
  372. .Callable("RangeFor")
  373. .Atom(0, ">=", TNodeFlags::Default)
  374. .Add(1, boundary)
  375. .Add(2, node->TailPtr())
  376. .Seal()
  377. .Build();
  378. rangeForIfNext = ctx.Builder(pos)
  379. // [boundary, next)
  380. .Callable("RangeIntersect")
  381. .Add(0, geBoundary)
  382. .Callable(1, "RangeFor")
  383. .Atom(0, "<", TNodeFlags::Default)
  384. .Add(1, next)
  385. .Add(2, node->TailPtr())
  386. .Seal()
  387. .Seal()
  388. .Build();
  389. // [boundary, +inf)
  390. rangeForIfNotNext = geBoundary;
  391. } else {
  392. const auto lessBoundary = ctx.Builder(pos)
  393. .Callable("RangeFor")
  394. .Atom(0, "<", TNodeFlags::Default)
  395. .Add(1, boundary)
  396. .Add(2, node->TailPtr())
  397. .Seal()
  398. .Build();
  399. rangeForIfNext = ctx.Builder(pos)
  400. // (-inf, boundary) U [next, +inf)
  401. .Callable("RangeUnion")
  402. .Add(0, lessBoundary)
  403. .Callable(1, "RangeFor")
  404. .Atom(0, ">=", TNodeFlags::Default)
  405. .Add(1, next)
  406. .Add(2, node->TailPtr())
  407. .Seal()
  408. .Seal()
  409. .Build();
  410. // (-inf, boundary)
  411. rangeForIfNotNext = lessBoundary;
  412. }
  413. auto body = ctx.Builder(pos)
  414. .Callable("If")
  415. .Callable(0, "HasNull")
  416. .Add(0, next)
  417. .Seal()
  418. .Add(1, rangeForIfNotNext)
  419. .Add(2, rangeForIfNext)
  420. .Seal()
  421. .Build();
  422. YQL_ENSURE(rangeForNullCast);
  423. result = ctx.Builder(pos)
  424. .Callable("IfPresent")
  425. .Callable(0, "StrictCast")
  426. .Add(0, value)
  427. .Add(1, ExpandType(pos, *ctx.MakeType<TOptionalExprType>(keyBaseType), ctx))
  428. .Seal()
  429. .Add(1, ctx.NewLambda(pos, ctx.NewArguments(pos, { boundary}), std::move(body)))
  430. .Add(2, rangeForNullCast)
  431. .Seal()
  432. .Build();
  433. } else {
  434. YQL_ENSURE(op == "<" || op == ">" || op == "<=" || op == ">=");
  435. result = ctx.Builder(pos)
  436. .Callable("If")
  437. .Callable(0, "HasNull")
  438. .Add(0, castedToKey)
  439. .Seal()
  440. .Callable(1, "IfPresent")
  441. .Callable(0, "SafeCast")
  442. .Add(0, value)
  443. .Add(1, ExpandType(pos, *ctx.MakeType<TOptionalExprType>(valueBaseType), ctx))
  444. .Seal()
  445. .Lambda(1)
  446. .Param("unwrappedValue")
  447. .Callable("IfPresent")
  448. .Callable(0, op.StartsWith("<") ? "RoundDown" : "RoundUp")
  449. .Arg(0, "unwrappedValue")
  450. .Add(1, ExpandType(pos, *keyBaseType, ctx))
  451. .Seal()
  452. .Lambda(1)
  453. .Param("roundedKey")
  454. .Apply(BuildNormalRangeLambdaRaw(pos, keyType, op.StartsWith("<") ? "<=" : ">=", ctx))
  455. .With(0)
  456. .Callable("SafeCast")
  457. .Arg(0, "roundedKey")
  458. .Add(1, ExpandType(pos, *optKeyType, ctx))
  459. .Seal()
  460. .Done()
  461. .Seal()
  462. .Seal()
  463. .Add(2, emptyRange)
  464. .Seal()
  465. .Seal()
  466. .Add(2, emptyRange)
  467. .Seal()
  468. .Apply(2, BuildNormalRangeLambdaRaw(pos, keyType, op, ctx))
  469. .With(0, castedToKey)
  470. .Seal()
  471. .Seal()
  472. .Build();
  473. }
  474. return result;
  475. }
  476. TExprNode::TPtr ExpandRangeToPg(const TExprNode::TPtr& node, TExprContext& ctx) {
  477. YQL_ENSURE(node->IsCallable("RangeToPg"));
  478. const size_t numComponents = node->Head().GetTypeAnn()->Cast<TListExprType>()->GetItemType()->
  479. Cast<TTupleExprType>()->GetItems().front()->Cast<TTupleExprType>()->GetSize();
  480. return ctx.Builder(node->Pos())
  481. .Callable("OrderedMap")
  482. .Add(0, node->HeadPtr())
  483. .Lambda(1)
  484. .Param("range")
  485. .Callable("StaticMap")
  486. .Arg(0, "range")
  487. .Lambda(1)
  488. .Param("boundary")
  489. .List()
  490. .Do([&](TExprNodeBuilder& parent) -> TExprNodeBuilder& {
  491. for (size_t i = 0; i < numComponents; ++i) {
  492. if (i % 2 == 0) {
  493. parent
  494. .Callable(i, "Nth")
  495. .Arg(0, "boundary")
  496. .Atom(1, i)
  497. .Seal();
  498. } else {
  499. parent
  500. .Callable(i, "Map")
  501. .Callable(0, "Nth")
  502. .Arg(0, "boundary")
  503. .Atom(1, i)
  504. .Seal()
  505. .Lambda(1)
  506. .Param("unwrapped")
  507. .Callable("ToPg")
  508. .Arg(0, "unwrapped")
  509. .Seal()
  510. .Seal()
  511. .Seal();
  512. }
  513. }
  514. return parent;
  515. })
  516. .Seal()
  517. .Seal()
  518. .Seal()
  519. .Seal()
  520. .Seal()
  521. .Build();
  522. }
  523. }