mkql_builtins_string_kernels.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. #include "mkql_builtins_string_kernels.h"
  2. #include "mkql_builtins_impl.h" // Y_IGNORE
  3. namespace NKikimr {
  4. namespace NMiniKQL {
  5. namespace {
  6. template <typename Return, typename... Args>
  7. constexpr auto GetArgumentsCount(Return(*)(Args...)) noexcept
  8. {
  9. return sizeof...(Args);
  10. }
  11. using TUntypedStringBinaryScalarFuncPtr = void(*)(std::string_view, std::string_view, void*);
  12. using TUntypedStringBinaryArrayFuncPtr = void(*)(const void* stringOffsets1, const void* data1, const void* stringOffsets2, const void* data2, void* resPtr, int64_t length, int64_t offset1, int64_t offset2);
  13. Y_NO_INLINE arrow::Status ExecStringScalarScalarImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  14. TPrimitiveDataTypeGetter typeGetter, TPrimitiveDataScalarGetter scalarGetter,
  15. TUntypedStringBinaryScalarFuncPtr func) {
  16. const auto& arg1 = batch.values[0];
  17. const auto& arg2 = batch.values[1];
  18. if (!arg1.scalar()->is_valid || !arg2.scalar()->is_valid) {
  19. *res = arrow::MakeNullScalar(typeGetter());
  20. } else {
  21. auto resDatum = scalarGetter();
  22. const auto resPtr = GetPrimitiveScalarValueMutablePtr(*resDatum.scalar());
  23. const auto val1 = GetStringScalarValue(*arg1.scalar());
  24. const auto val2 = GetStringScalarValue(*arg2.scalar());
  25. func(val1, val2, resPtr);
  26. *res = resDatum.scalar();
  27. }
  28. return arrow::Status::OK();
  29. }
  30. Y_NO_INLINE arrow::Status ExecStringScalarArrayImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  31. TUntypedStringBinaryArrayFuncPtr func) {
  32. const auto& arg1 = batch.values[0];
  33. const auto& arg2 = batch.values[1];
  34. auto& resArr = *res->array();
  35. if (arg1.scalar()->is_valid) {
  36. const auto val1 = GetStringScalarValue(*arg1.scalar());
  37. const auto& arr2 = *arg2.array();
  38. auto length = arr2.length;
  39. auto resPtr = resArr.buffers[1]->mutable_data();
  40. const size_t val1Size = val1.size();
  41. const auto offsets2 = arr2.buffers[1]->data();
  42. const auto data2 = arr2.buffers[2]->data();
  43. func(&val1Size, val1.data(), offsets2, data2, resPtr, length, 0, arr2.offset);
  44. }
  45. return arrow::Status::OK();
  46. }
  47. Y_NO_INLINE arrow::Status ExecStringArrayScalarImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  48. TUntypedStringBinaryArrayFuncPtr func) {
  49. const auto& arg1 = batch.values[0];
  50. const auto& arg2 = batch.values[1];
  51. auto& resArr = *res->array();
  52. if (arg2.scalar()->is_valid) {
  53. const auto val2 = GetStringScalarValue(*arg2.scalar());
  54. const auto& arr1 = *arg1.array();
  55. auto length = arr1.length;
  56. auto resPtr = resArr.buffers[1]->mutable_data();
  57. const size_t val2Size = val2.size();
  58. const auto offsets1 = arr1.buffers[1]->data();
  59. const auto data1 = arr1.buffers[2]->data();
  60. func(offsets1, data1, &val2Size, val2.data(), resPtr, length, arr1.offset, 0);
  61. }
  62. return arrow::Status::OK();
  63. }
  64. Y_NO_INLINE arrow::Status ExecStringArrayArrayImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  65. TUntypedStringBinaryArrayFuncPtr func) {
  66. const auto& arg1 = batch.values[0];
  67. const auto& arg2 = batch.values[1];
  68. const auto& arr1 = *arg1.array();
  69. const auto& arr2 = *arg2.array();
  70. auto& resArr = *res->array();
  71. MKQL_ENSURE(arr1.length == arr2.length, "Expected same length");
  72. auto length = arr1.length;
  73. auto resPtr = resArr.buffers[1]->mutable_data();
  74. const auto offsets1 = arr1.buffers[1]->data();
  75. const auto offsets2 = arr2.buffers[1]->data();
  76. const auto data1 = arr1.buffers[2]->data();
  77. const auto data2 = arr2.buffers[2]->data();
  78. func(offsets1, data1, offsets2, data2, resPtr, length, arr1.offset, arr2.offset);
  79. return arrow::Status::OK();
  80. }
  81. Y_NO_INLINE arrow::Status ExecStringBinaryImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  82. TPrimitiveDataTypeGetter typeGetter, TPrimitiveDataScalarGetter scalarGetter,
  83. TUntypedStringBinaryScalarFuncPtr scalarScalarFunc,
  84. TUntypedStringBinaryArrayFuncPtr scalarArrayFunc,
  85. TUntypedStringBinaryArrayFuncPtr arrayScalarFunc,
  86. TUntypedStringBinaryArrayFuncPtr arrayArrayFunc) {
  87. MKQL_ENSURE(batch.values.size() == 2, "Expected 2 args");
  88. const auto& arg1 = batch.values[0];
  89. const auto& arg2 = batch.values[1];
  90. if (arg1.is_scalar()) {
  91. if (arg2.is_scalar()) {
  92. return ExecStringScalarScalarImpl(batch, res, typeGetter, scalarGetter, scalarScalarFunc);
  93. } else {
  94. return ExecStringScalarArrayImpl(batch, res, scalarArrayFunc);
  95. }
  96. } else {
  97. if (arg2.is_scalar()) {
  98. return ExecStringArrayScalarImpl(batch, res, arrayScalarFunc);
  99. } else {
  100. return ExecStringArrayArrayImpl(batch, res, arrayArrayFunc);
  101. }
  102. }
  103. }
  104. template<typename TInput1, typename TInput2, typename TOutput, class TOp>
  105. struct TBinaryStringExecs
  106. {
  107. using TOffset1 = typename TPrimitiveDataType<TInput1>::TResult::offset_type;
  108. using TOffset2 = typename TPrimitiveDataType<TInput2>::TResult::offset_type;
  109. using TTypedStringBinaryScalarFuncPtr = void(*)(std::string_view, std::string_view, TOutput*);
  110. using TTypedStringBinaryArrayFuncPtr = void(*)(const TOffset1* stringOffsets1, const char* data1,
  111. const TOffset2* stringOffsets2, const char* data2, TOutput* resPtr, int64_t length, int64_t offset1, int64_t offset2);
  112. static void ScalarScalarCore(std::string_view arg1, std::string_view arg2, TOutput* resPtr) {
  113. *resPtr = TOp::Do(arg1, arg2);
  114. }
  115. static void ScalarArrayCore(const TOffset1* stringOffsets1, const char* data1,
  116. const TOffset2* stringOffsets2, const char* data2, TOutput* resPtr, int64_t length, int64_t offset1, int64_t offset2) {
  117. Y_UNUSED(offset1);
  118. const auto val1 = std::string_view(data1, *(const size_t*)stringOffsets1);
  119. stringOffsets2 += offset2;
  120. if (val1.empty()) {
  121. if constexpr (GetArgumentsCount(TOp::DoWithEmptyLeft) == 0) {
  122. std::fill(resPtr, resPtr + length, TOp::DoWithEmptyLeft());
  123. } else {
  124. for (int64_t i = 0; i < length; ++i, ++resPtr, ++stringOffsets2) {
  125. *resPtr = TOp::DoWithEmptyLeft(stringOffsets2[1] - stringOffsets2[0]);
  126. }
  127. }
  128. } else {
  129. for (int64_t i = 0; i < length; ++i, ++resPtr, ++stringOffsets2) {
  130. std::string_view val2(data2 + stringOffsets2[0], stringOffsets2[1] - stringOffsets2[0]);
  131. *resPtr = TOp::Do(val1, val2);
  132. }
  133. }
  134. }
  135. static void ArrayScalarCore(const TOffset1* stringOffsets1, const char* data1,
  136. const TOffset2* stringOffsets2, const char* data2, TOutput* resPtr, int64_t length, int64_t offset1, int64_t offset2) {
  137. Y_UNUSED(offset2);
  138. const auto val2 = std::string_view(data2, *(const size_t*)stringOffsets2);
  139. stringOffsets1 += offset1;
  140. if (val2.empty()) {
  141. if constexpr (GetArgumentsCount(TOp::DoWithEmptyRight) == 0) {
  142. std::fill(resPtr, resPtr + length, TOp::DoWithEmptyRight());
  143. } else {
  144. for (int64_t i = 0; i < length; ++i, ++resPtr, ++stringOffsets1) {
  145. *resPtr = TOp::DoWithEmptyRight(stringOffsets1[1] - stringOffsets1[0]);
  146. }
  147. }
  148. } else {
  149. for (int64_t i = 0; i < length; ++i, ++resPtr, ++stringOffsets1) {
  150. std::string_view val1(data1 + stringOffsets1[0], stringOffsets1[1] - stringOffsets1[0]);
  151. *resPtr = TOp::Do(val1, val2);
  152. }
  153. }
  154. }
  155. static void ArrayArrayCore(const TOffset1* stringOffsets1, const char* data1,
  156. const TOffset2* stringOffsets2, const char* data2, TOutput* resPtr, int64_t length, int64_t offset1, int64_t offset2) {
  157. stringOffsets1 += offset1;
  158. stringOffsets2 += offset2;
  159. for (int64_t i = 0; i < length; ++i, ++stringOffsets1, ++stringOffsets2, ++resPtr) {
  160. std::string_view val1(data1 + stringOffsets1[0], stringOffsets1[1] - stringOffsets1[0]);
  161. std::string_view val2(data2 + stringOffsets2[0], stringOffsets2[1] - stringOffsets2[0]);
  162. *resPtr = TOp::Do(val1, val2);
  163. }
  164. }
  165. static arrow::Status Exec(arrow::compute::KernelContext*, const arrow::compute::ExecBatch& batch, arrow::Datum* res) {
  166. static_assert(!std::is_same<TOutput, bool>::value);
  167. TTypedStringBinaryScalarFuncPtr scalarScalarFunc = &ScalarScalarCore;
  168. TTypedStringBinaryArrayFuncPtr scalarArrayFunc = &ScalarArrayCore;
  169. TTypedStringBinaryArrayFuncPtr arrayScalarFunc = &ArrayScalarCore;
  170. TTypedStringBinaryArrayFuncPtr arrayArrayFunc = &ArrayArrayCore;
  171. return ExecStringBinaryImpl(batch, res, &GetPrimitiveDataType<TOutput>,
  172. &MakeDefaultScalarDatum<TOutput>,
  173. (TUntypedStringBinaryScalarFuncPtr)scalarScalarFunc,
  174. (TUntypedStringBinaryArrayFuncPtr)scalarArrayFunc,
  175. (TUntypedStringBinaryArrayFuncPtr)arrayScalarFunc,
  176. (TUntypedStringBinaryArrayFuncPtr)arrayArrayFunc);
  177. }
  178. };
  179. using TUntypedStringUnaryScalarFuncPtr = void(*)(std::string_view, void*);
  180. using TUntypedStringUnaryArrayFuncPtr = void(*)(const void* stringOffsets, const void* data, void* resPtr, int64_t length, int64_t offset);
  181. Y_NO_INLINE arrow::Status ExecStringScalarImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  182. TPrimitiveDataTypeGetter typeGetter, TPrimitiveDataScalarGetter scalarGetter,
  183. TUntypedStringUnaryScalarFuncPtr func) {
  184. const auto& arg = batch.values[0];
  185. if (!arg.scalar()->is_valid) {
  186. *res = arrow::MakeNullScalar(typeGetter());
  187. } else {
  188. auto resDatum = scalarGetter();
  189. const auto resPtr = GetPrimitiveScalarValueMutablePtr(*resDatum.scalar());
  190. const auto val = GetStringScalarValue(*arg.scalar());
  191. func(val, resPtr);
  192. *res = resDatum.scalar();
  193. }
  194. return arrow::Status::OK();
  195. }
  196. Y_NO_INLINE arrow::Status ExecStringArrayImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  197. TUntypedStringUnaryArrayFuncPtr func) {
  198. const auto& arg = batch.values[0];
  199. auto& resArr = *res->array();
  200. const auto& arr = *arg.array();
  201. const auto length = arr.length;
  202. const auto resValues = resArr.buffers[1]->mutable_data();
  203. const auto offsets = arr.buffers[1]->data();
  204. const auto data = arr.buffers[2]->data();
  205. func(offsets, data, resValues, length, arr.offset);
  206. return arrow::Status::OK();
  207. }
  208. Y_NO_INLINE arrow::Status ExecStringUnaryImpl(const arrow::compute::ExecBatch& batch, arrow::Datum* res,
  209. TPrimitiveDataTypeGetter typeGetter, TPrimitiveDataScalarGetter scalarGetter,
  210. TUntypedStringUnaryScalarFuncPtr scalarFunc,
  211. TUntypedStringUnaryArrayFuncPtr arrayFunc) {
  212. MKQL_ENSURE(batch.values.size() == 1, "Expected single argument");
  213. const auto& arg = batch.values[0];
  214. if (arg.is_scalar()) {
  215. return ExecStringScalarImpl(batch, res, typeGetter, scalarGetter, scalarFunc);
  216. } else {
  217. return ExecStringArrayImpl(batch, res, arrayFunc);
  218. }
  219. }
  220. template<typename TInput, typename TOutput, class TOp>
  221. struct TUnaryStringExecs
  222. {
  223. using TOffset = typename TPrimitiveDataType<TInput>::TResult::offset_type;
  224. using TTypedStringUnaryScalarFuncPtr = void(*)(std::string_view, TOutput* resPtr);
  225. using TTypedStringUnaryArrayFuncPtr = void(*)(const TOffset* offsets, const char* data, TOutput* resPtr, int64_t length, int64_t offset);
  226. static void ScalarCore(std::string_view arg, TOutput* resPtr) {
  227. *resPtr = TOp::Do(arg);
  228. }
  229. static void ArrayCore(const TOffset* stringOffsets, const char* data, TOutput* resPtr, int64_t length, int64_t offset) {
  230. stringOffsets += offset;
  231. for (int64_t i = 0; i < length; ++i, ++stringOffsets, ++resPtr) {
  232. std::string_view val(data + stringOffsets[0], stringOffsets[1] - stringOffsets[0]);
  233. *resPtr = TOp::Do(val);
  234. }
  235. }
  236. static arrow::Status Exec(arrow::compute::KernelContext*, const arrow::compute::ExecBatch& batch, arrow::Datum* res) {
  237. TTypedStringUnaryScalarFuncPtr scalarFunc = &ScalarCore;
  238. TTypedStringUnaryArrayFuncPtr arrayFunc = &ArrayCore;
  239. return ExecStringUnaryImpl(batch, res, &GetPrimitiveDataType<TOutput>, &MakeDefaultScalarDatum<TOutput>,
  240. (TUntypedStringUnaryScalarFuncPtr)scalarFunc,
  241. (TUntypedStringUnaryArrayFuncPtr)arrayFunc);
  242. }
  243. };
  244. // -------------------------------------------------------------------------------------
  245. // String comparison
  246. // -------------------------------------------------------------------------------------
  247. struct TStrEqualsOp {
  248. static inline bool Do(std::string_view left, std::string_view right) {
  249. return left == right;
  250. }
  251. static inline bool DoWithEmptyLeft(size_t rightLen) {
  252. return rightLen == 0;
  253. }
  254. static inline bool DoWithEmptyRight(size_t leftLen) {
  255. return leftLen == 0;
  256. }
  257. };
  258. struct TStrNotEqualsOp {
  259. static inline bool Do(std::string_view left, std::string_view right) {
  260. return left != right;
  261. }
  262. static inline bool DoWithEmptyLeft(size_t rightLen) {
  263. return rightLen != 0;
  264. }
  265. static inline bool DoWithEmptyRight(size_t leftLen) {
  266. return leftLen != 0;
  267. }
  268. };
  269. struct TStrLessOp {
  270. static inline bool Do(std::string_view left, std::string_view right) {
  271. return left < right;
  272. }
  273. static inline bool DoWithEmptyLeft(size_t rightLen) {
  274. return rightLen != 0;
  275. }
  276. static constexpr bool DoWithEmptyRight() {
  277. return false;
  278. }
  279. };
  280. struct TStrLessOrEqualOp {
  281. static inline bool Do(std::string_view left, std::string_view right) {
  282. return left <= right;
  283. }
  284. static constexpr bool DoWithEmptyLeft() {
  285. return true;
  286. }
  287. static inline bool DoWithEmptyRight(size_t leftLen) {
  288. return leftLen == 0;
  289. }
  290. };
  291. struct TStrGreaterOp {
  292. static inline bool Do(std::string_view left, std::string_view right) {
  293. return left > right;
  294. }
  295. static constexpr bool DoWithEmptyLeft() {
  296. return false;
  297. }
  298. static inline bool DoWithEmptyRight(size_t leftLen) {
  299. return leftLen != 0;
  300. }
  301. };
  302. struct TStrGreaterOrEqualOp {
  303. static inline bool Do(std::string_view left, std::string_view right) {
  304. return left >= right;
  305. }
  306. static inline bool DoWithEmptyLeft(size_t rightLen) {
  307. return rightLen == 0;
  308. }
  309. static constexpr bool DoWithEmptyRight() {
  310. return true;
  311. }
  312. };
  313. struct TStrStartsWithOp {
  314. static inline bool Do(std::string_view left, std::string_view right) {
  315. return left.starts_with(right);
  316. }
  317. static inline bool DoWithEmptyLeft(size_t rightLen) {
  318. return rightLen == 0;
  319. }
  320. static constexpr bool DoWithEmptyRight() {
  321. return true;
  322. }
  323. };
  324. struct TStrEndsWithOp {
  325. static inline bool Do(std::string_view left, std::string_view right) {
  326. return left.ends_with(right);
  327. }
  328. static inline bool DoWithEmptyLeft(size_t rightLen) {
  329. return rightLen == 0;
  330. }
  331. static constexpr bool DoWithEmptyRight() {
  332. return true;
  333. }
  334. };
  335. struct TStrContainsOp {
  336. static inline bool Do(std::string_view left, std::string_view right) {
  337. return left.contains(right);
  338. }
  339. static inline bool DoWithEmptyLeft(size_t rightLen) {
  340. return rightLen == 0;
  341. }
  342. static constexpr bool DoWithEmptyRight() {
  343. return true;
  344. }
  345. };
  346. Y_NO_INLINE void AddCompareStringKernelImpl(TKernelFamilyBase& kernelFamily, NUdf::TDataTypeId type1, NUdf::TDataTypeId type2,
  347. const arrow::compute::ArrayKernelExec& exec, arrow::compute::InputType&& inputType1, arrow::compute::InputType&& inputType2,
  348. arrow::compute::OutputType&& outputType) {
  349. std::vector<NUdf::TDataTypeId> argTypes({ type1, type2 });
  350. NUdf::TDataTypeId returnType = NUdf::TDataType<bool>::Id;
  351. auto k = std::make_unique<arrow::compute::ScalarKernel>(std::vector<arrow::compute::InputType>{
  352. inputType1, inputType2
  353. }, outputType, exec);
  354. k->null_handling = arrow::compute::NullHandling::INTERSECTION;
  355. kernelFamily.Adopt(argTypes, returnType, std::make_unique<TPlainKernel>(kernelFamily, argTypes, returnType, std::move(k), TKernel::ENullMode::Default));
  356. }
  357. template<typename TInput1, typename TInput2, typename TOp>
  358. void AddCompareStringKernel(TKernelFamilyBase& kernelFamily) {
  359. // ui8 type is used as bool replacement
  360. using TOutput = ui8;
  361. using TExecs = TBinaryStringExecs<TInput1, TInput2, TOutput, TOp>;
  362. AddCompareStringKernelImpl(kernelFamily, NUdf::TDataType<TInput1>::Id, NUdf::TDataType<TInput2>::Id, &TExecs::Exec,
  363. GetPrimitiveInputArrowType<TInput1>(), GetPrimitiveInputArrowType<TInput2>(), GetPrimitiveOutputArrowType<TOutput>()
  364. );
  365. }
  366. template<typename TOp>
  367. void AddCompareStringKernels(TKernelFamilyBase& kernelFamily) {
  368. AddCompareStringKernel<char*, char*, TOp>(kernelFamily);
  369. AddCompareStringKernel<char*, NUdf::TUtf8, TOp>(kernelFamily);
  370. AddCompareStringKernel<NUdf::TUtf8, char*, TOp>(kernelFamily);
  371. AddCompareStringKernel<NUdf::TUtf8, NUdf::TUtf8, TOp>(kernelFamily);
  372. }
  373. // -------------------------------------------------------------------------------------
  374. // String size
  375. // -------------------------------------------------------------------------------------
  376. template<typename TOutput>
  377. struct TStrSizeOp {
  378. static inline TOutput Do(std::string_view input) {
  379. return static_cast<TOutput>(input.size());
  380. }
  381. };
  382. Y_NO_INLINE void AddSizeStringKernelImpl(TKernelFamilyBase& kernelFamily, NUdf::TDataTypeId type1, NUdf::TDataTypeId returnType,
  383. const arrow::compute::ArrayKernelExec& exec, arrow::compute::InputType&& inputType1, arrow::compute::OutputType&& outputType) {
  384. std::vector<NUdf::TDataTypeId> argTypes({ type1 });
  385. auto k = std::make_unique<arrow::compute::ScalarKernel>(std::vector<arrow::compute::InputType>{
  386. inputType1
  387. }, outputType, exec);
  388. k->null_handling = arrow::compute::NullHandling::INTERSECTION;
  389. kernelFamily.Adopt(argTypes, returnType, std::make_unique<TPlainKernel>(kernelFamily, argTypes, returnType, std::move(k), TKernel::ENullMode::Default));
  390. }
  391. template<typename TInput>
  392. void AddSizeStringKernel(TKernelFamilyBase& kernelFamily) {
  393. using TOutput = ui32;
  394. using TOp = TStrSizeOp<TOutput>;
  395. using TExecs = TUnaryStringExecs<TInput, TOutput, TOp>;
  396. AddSizeStringKernelImpl(kernelFamily, NUdf::TDataType<TInput>::Id, NUdf::TDataType<TOutput>::Id, &TExecs::Exec,
  397. GetPrimitiveInputArrowType<TInput>(), GetPrimitiveOutputArrowType<TOutput>());
  398. }
  399. } // namespace
  400. void RegisterStringKernelEquals(TKernelFamilyBase& kernelFamily) {
  401. AddCompareStringKernels<TStrEqualsOp>(kernelFamily);
  402. }
  403. void RegisterStringKernelNotEquals(TKernelFamilyBase& kernelFamily) {
  404. AddCompareStringKernels<TStrNotEqualsOp>(kernelFamily);
  405. }
  406. void RegisterStringKernelLess(TKernelFamilyBase& kernelFamily) {
  407. AddCompareStringKernels<TStrLessOp>(kernelFamily);
  408. }
  409. void RegisterStringKernelLessOrEqual(TKernelFamilyBase& kernelFamily) {
  410. AddCompareStringKernels<TStrLessOrEqualOp>(kernelFamily);
  411. }
  412. void RegisterStringKernelGreater(TKernelFamilyBase& kernelFamily) {
  413. AddCompareStringKernels<TStrGreaterOp>(kernelFamily);
  414. }
  415. void RegisterStringKernelGreaterOrEqual(TKernelFamilyBase& kernelFamily) {
  416. AddCompareStringKernels<TStrGreaterOrEqualOp>(kernelFamily);
  417. }
  418. void RegisterStringKernelSize(TKernelFamilyBase& kernelFamily) {
  419. AddSizeStringKernel<char*>(kernelFamily);
  420. AddSizeStringKernel<NUdf::TUtf8>(kernelFamily);
  421. }
  422. void RegisterStringKernelStartsWith(TKernelFamilyBase& kernelFamily) {
  423. AddCompareStringKernels<TStrStartsWithOp>(kernelFamily);
  424. }
  425. void RegisterStringKernelEndsWith(TKernelFamilyBase& kernelFamily) {
  426. AddCompareStringKernels<TStrEndsWithOp>(kernelFamily);
  427. }
  428. void RegisterStringKernelContains(TKernelFamilyBase& kernelFamily) {
  429. AddCompareStringKernels<TStrContainsOp>(kernelFamily);
  430. }
  431. void RegisterSizeBuiltin(TKernelFamilyMap& kernelFamilyMap) {
  432. auto family = std::make_unique<TKernelFamilyBase>();
  433. RegisterStringKernelSize(*family);
  434. kernelFamilyMap["Size"] = std::move(family);
  435. }
  436. void RegisterWith(TKernelFamilyMap& kernelFamilyMap) {
  437. auto family = std::make_unique<TKernelFamilyBase>();
  438. RegisterStringKernelStartsWith(*family);
  439. kernelFamilyMap["StartsWith"] = std::move(family);
  440. family = std::make_unique<TKernelFamilyBase>();
  441. RegisterStringKernelEndsWith(*family);
  442. kernelFamilyMap["EndsWith"] = std::move(family);
  443. family = std::make_unique<TKernelFamilyBase>();
  444. RegisterStringKernelContains(*family);
  445. kernelFamilyMap["StringContains"] = std::move(family);
  446. }
  447. }
  448. }