udf_helpers.h 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. #pragma once
  2. #include "udf_value.h"
  3. #include "udf_registrator.h"
  4. #include "udf_value_builder.h"
  5. #include "udf_terminator.h"
  6. #include "udf_type_builder.h"
  7. #include "udf_type_inspection.h"
  8. #include "udf_version.h"
  9. #include "udf_type_printer.h"
  10. #include <util/generic/yexception.h>
  11. #include <util/generic/string.h>
  12. #include <util/generic/strbuf.h>
  13. #include <util/string/builder.h>
  14. namespace NYql {
  15. namespace NUdf {
  16. inline TSourcePosition GetSourcePosition(IFunctionTypeInfoBuilder& builder) {
  17. #if UDF_ABI_COMPATIBILITY_VERSION_CURRENT >= UDF_ABI_COMPATIBILITY_VERSION(2, 9)
  18. return builder.GetSourcePosition();
  19. #else
  20. Y_UNUSED(builder);
  21. return {};
  22. #endif
  23. }
  24. TString LoadResourceOnce(TStringBuf resourceId);
  25. inline void SetIRImplementation(IFunctionTypeInfoBuilder& builder, TStringBuf resourceId, TStringBuf functionName) {
  26. #if UDF_ABI_COMPATIBILITY_VERSION_CURRENT >= UDF_ABI_COMPATIBILITY_VERSION(2, 16)
  27. if (functionName) {
  28. builder.IRImplementation(LoadResourceOnce(resourceId), resourceId, functionName);
  29. }
  30. #else
  31. Y_UNUSED(builder);
  32. Y_UNUSED(resourceId);
  33. Y_UNUSED(functionName);
  34. #endif
  35. }
  36. }
  37. }
  38. #define UDF_IMPL_EX(udfName, typeBody, members, init, irResourceId, irFunctionName, blockType, create_impl) \
  39. class udfName: public ::NYql::NUdf::TBoxedValue { \
  40. public: \
  41. using TBlockType = blockType; \
  42. explicit udfName(::NYql::NUdf::IFunctionTypeInfoBuilder& builder) \
  43. : Pos_(GetSourcePosition(builder)) \
  44. { \
  45. init \
  46. } \
  47. static const ::NYql::NUdf::TStringRef& Name() { \
  48. static auto name = ::NYql::NUdf::TStringRef::Of(#udfName).Substring(1, 256); \
  49. return name; \
  50. } \
  51. inline ::NYql::NUdf::TUnboxedValue RunImpl( \
  52. const ::NYql::NUdf::IValueBuilder* valueBuilder, \
  53. const ::NYql::NUdf::TUnboxedValuePod* args) const; \
  54. ::NYql::NUdf::TUnboxedValue Run( \
  55. const ::NYql::NUdf::IValueBuilder* valueBuilder, \
  56. const ::NYql::NUdf::TUnboxedValuePod* args) const override { \
  57. try { \
  58. return RunImpl(valueBuilder, args); \
  59. } catch (const std::exception&) { \
  60. TStringBuilder sb; \
  61. sb << Pos_ << " "; \
  62. sb << CurrentExceptionMessage(); \
  63. sb << Endl << "[" << TStringBuf(Name()) << "]" ; \
  64. UdfTerminate(sb.c_str()); \
  65. } \
  66. } \
  67. static bool DeclareSignature( \
  68. const ::NYql::NUdf::TStringRef& name, \
  69. ::NYql::NUdf::TType* userType, \
  70. ::NYql::NUdf::IFunctionTypeInfoBuilder& builder, \
  71. bool typesOnly) { \
  72. Y_UNUSED(userType); \
  73. if (Name() == name) { \
  74. typeBody if (!typesOnly) { \
  75. create_impl \
  76. SetIRImplementation(builder, irResourceId, irFunctionName); \
  77. } \
  78. return true; \
  79. } \
  80. return false; \
  81. } \
  82. ::NYql::NUdf::TSourcePosition GetPos() const { return Pos_; } \
  83. private: \
  84. ::NYql::NUdf::TSourcePosition Pos_; \
  85. members \
  86. }; \
  87. ::NYql::NUdf::TUnboxedValue udfName::RunImpl( \
  88. const ::NYql::NUdf::IValueBuilder* valueBuilder, \
  89. const ::NYql::NUdf::TUnboxedValuePod* args) const
  90. #define UDF_IMPL(udfName, typeBody, members, init, irResourceId, irFunctionName, blockType) \
  91. UDF_IMPL_EX(udfName, typeBody, members, init, irResourceId, irFunctionName, blockType, builder.Implementation(new udfName(builder));)
  92. #define UDF(udfName, typeBody) UDF_IMPL(udfName, typeBody, ;, ;, "", "", void)
  93. #define UDF_RUN_IMPL(udfName, typeBody, members, init, irResourceId, irFunctionName) \
  94. struct udfName##Members { \
  95. members \
  96. }; \
  97. \
  98. class udfName: public ::NYql::NUdf::TBoxedValue, public udfName##Members { \
  99. public: \
  100. explicit udfName(::NYql::NUdf::IFunctionTypeInfoBuilder& builder) \
  101. : Pos_(GetSourcePosition(builder)) \
  102. { \
  103. init \
  104. } \
  105. static const ::NYql::NUdf::TStringRef& Name() { \
  106. static auto name = ::NYql::NUdf::TStringRef::Of(#udfName).Substring(1, 256); \
  107. return name; \
  108. } \
  109. class TImpl: public TBoxedValue, public udfName##Members { \
  110. public: \
  111. TImpl(const udfName##Members& parent, \
  112. const ::NYql::NUdf::TUnboxedValuePod& runConfig, \
  113. ::NYql::NUdf::TSourcePosition pos) \
  114. : udfName##Members(parent) \
  115. , RunConfig(::NYql::NUdf::TUnboxedValuePod(runConfig)) \
  116. , Pos_(pos) \
  117. {} \
  118. inline ::NYql::NUdf::TUnboxedValue RunImpl( \
  119. const ::NYql::NUdf::IValueBuilder* valueBuilder, \
  120. const ::NYql::NUdf::TUnboxedValuePod* args) const; \
  121. ::NYql::NUdf::TUnboxedValue Run( \
  122. const ::NYql::NUdf::IValueBuilder* valueBuilder, \
  123. const ::NYql::NUdf::TUnboxedValuePod* args) const override { \
  124. try { \
  125. return RunImpl(valueBuilder, args); \
  126. } catch (const std::exception&) { \
  127. TStringBuilder sb; \
  128. sb << Pos_ << " "; \
  129. sb << CurrentExceptionMessage(); \
  130. sb << Endl << "[" << TStringBuf(Name()) << "]" ; \
  131. UdfTerminate(sb.c_str()); \
  132. } \
  133. } \
  134. \
  135. private: \
  136. ::NYql::NUdf::TUnboxedValue RunConfig; \
  137. ::NYql::NUdf::TSourcePosition Pos_; \
  138. }; \
  139. ::NYql::NUdf::TUnboxedValue Run( \
  140. const ::NYql::NUdf::IValueBuilder* valueBuilder, \
  141. const ::NYql::NUdf::TUnboxedValuePod* args) const override { \
  142. Y_UNUSED(valueBuilder); \
  143. return ::NYql::NUdf::TUnboxedValuePod(new TImpl(*this, args[0], Pos_)); \
  144. } \
  145. static bool DeclareSignature( \
  146. const ::NYql::NUdf::TStringRef& name, \
  147. ::NYql::NUdf::TType* userType, \
  148. ::NYql::NUdf::IFunctionTypeInfoBuilder& builder, \
  149. bool typesOnly) { \
  150. Y_UNUSED(userType); \
  151. if (Name() == name) { \
  152. typeBody if (!typesOnly) { \
  153. builder.Implementation(new udfName(builder)); \
  154. SetIRImplementation(builder, irResourceId, irFunctionName); \
  155. } \
  156. return true; \
  157. } \
  158. return false; \
  159. } \
  160. private: \
  161. ::NYql::NUdf::TSourcePosition Pos_; \
  162. }; \
  163. ::NYql::NUdf::TUnboxedValue udfName::TImpl::RunImpl( \
  164. const ::NYql::NUdf::IValueBuilder* valueBuilder, \
  165. const ::NYql::NUdf::TUnboxedValuePod* args) const
  166. #define UDF_RUN(udfName, typeBody) UDF_RUN_IMPL(udfName, typeBody, ;, ;, "", "")
  167. #define SIMPLE_UDF_IMPL_EX(udfName, typeBody, signature, irResourceId, irFunctionName, blockType, create_impl) \
  168. UDF_IMPL_EX(udfName, typeBody, const ::NYql::NUdf::TType* ReturnType_;, ReturnType_ = ::NYql::NUdf::NImpl::TSimpleSignatureHelper<signature>::BuildReturnType(builder);, irResourceId, irFunctionName, blockType, create_impl)
  169. #define SIMPLE_UDF_IMPL(udfName, typeBody, signature, irResourceId, irFunctionName, blockType) \
  170. SIMPLE_UDF_IMPL_EX(udfName, typeBody, signature, irResourceId, irFunctionName, blockType, builder.Implementation(new udfName(builder));)
  171. #define SIMPLE_UDF(udfName, signature) \
  172. SIMPLE_UDF_IMPL(udfName, builder.SimpleSignature<signature>();, signature, "", "", void)
  173. #define SIMPLE_STRICT_UDF(udfName, signature) \
  174. SIMPLE_UDF_IMPL(udfName, builder.SimpleSignature<signature>().IsStrict();, signature, "", "", void)
  175. #define SIMPLE_STRICT_UDF_WITH_IR(udfName, signature, optionalArgsCount, irResourceId, irFunctionName) \
  176. SIMPLE_UDF_IMPL(udfName, builder.SimpleSignature<signature>().OptionalArgs(optionalArgsCount).IsStrict();, signature, irResourceId, irFunctionName, void)
  177. #define SIMPLE_UDF_WITH_CREATE_IMPL(udfName, signature, create_impl) \
  178. SIMPLE_UDF_IMPL_EX(udfName, builder.SimpleSignature<signature>();, signature, "", "", void, create_impl)
  179. #define SIMPLE_UDF_OPTIONS(udfName, signature, options) \
  180. SIMPLE_UDF_IMPL(udfName, builder.SimpleSignature<signature>(); options;, signature, "", "", void)
  181. #define SIMPLE_UDF_WITH_OPTIONAL_ARGS(udfName, signature, optionalArgsCount) \
  182. SIMPLE_UDF_IMPL(udfName, builder.SimpleSignature<signature>().OptionalArgs(optionalArgsCount);, signature, "", "", void)
  183. #define SIMPLE_STRICT_UDF_OPTIONS(udfName, signature, options) \
  184. SIMPLE_UDF_IMPL(udfName, builder.SimpleSignature<signature>().IsStrict(); options;, signature, "", "", void)
  185. #define SIMPLE_STRICT_UDF_WITH_OPTIONAL_ARGS(udfName, signature, optionalArgsCount) \
  186. SIMPLE_UDF_IMPL(udfName, builder.SimpleSignature<signature>().OptionalArgs(optionalArgsCount).IsStrict();, signature, "", "", void)
  187. #define SIMPLE_UDF_RUN_OPTIONS(udfName, signature, options) \
  188. UDF_RUN(udfName, builder.SimpleSignature<signature>(); options;)
  189. #define SIMPLE_UDF_RUN(udfName, signature, runConfigSignature) \
  190. SIMPLE_UDF_RUN_OPTIONS(udfName, signature, builder.RunConfig<runConfigSignature>())
  191. #define SIMPLE_MODULE(moduleName, ...) \
  192. class moduleName: public ::NYql::NUdf::TSimpleUdfModuleHelper<__VA_ARGS__> { \
  193. public: \
  194. ::NYql::NUdf::TStringRef Name() const { \
  195. auto name = ::NYql::NUdf::TStringRef::Of(#moduleName); \
  196. return name.Substring(1, name.Size() - 7); \
  197. } \
  198. };
  199. #define EMPTY_RESULT_ON_EMPTY_ARG(n) \
  200. if (!args[n]) { \
  201. return ::NYql::NUdf::TUnboxedValue(); \
  202. }
  203. namespace NYql {
  204. namespace NUdf {
  205. template<bool CheckOptional, bool CheckBlock, const char* TFuncName, template<class> class TFunc, typename... TUserTypes>
  206. class TUserDataTypeFuncFactory : public ::NYql::NUdf::TBoxedValue {
  207. public:
  208. typedef bool TTypeAwareMarker;
  209. public:
  210. static const ::NYql::NUdf::TStringRef& Name() {
  211. static auto name = ::NYql::NUdf::TStringRef(TFuncName, std::strlen(TFuncName));
  212. return name;
  213. }
  214. static const TType* ExtractArgFromUserType(::NYql::NUdf::TType const* userType, ::NYql::NUdf::IFunctionTypeInfoBuilder& builder) {
  215. if constexpr (CheckBlock) {
  216. #if UDF_ABI_COMPATIBILITY_VERSION_CURRENT >= UDF_ABI_COMPATIBILITY_VERSION(2, 26)
  217. TBlockTypeInspector block(*builder.TypeInfoHelper(), userType);
  218. if (block) {
  219. userType = block.GetItemType();
  220. }
  221. #endif
  222. }
  223. if constexpr (CheckOptional) {
  224. TOptionalTypeInspector optionalTypeInspector(*builder.TypeInfoHelper(), userType);
  225. if (optionalTypeInspector) {
  226. userType = optionalTypeInspector.GetItemType();
  227. }
  228. }
  229. return userType;
  230. }
  231. template<typename TUserType>
  232. static bool DeclareSignatureImpl(
  233. const ::NYql::NUdf::TStringRef& name,
  234. TDataTypeId typeId,
  235. ::NYql::NUdf::TType* userType,
  236. ::NYql::NUdf::IFunctionTypeInfoBuilder& builder,
  237. bool typesOnly)
  238. {
  239. if (TDataType<TUserType>::Id != typeId) {
  240. return false;
  241. }
  242. TFunc<TUserType>::DeclareSignature(name, userType, builder, typesOnly);
  243. return true;
  244. }
  245. template<typename TUserType, typename THead, typename... TTail>
  246. static bool DeclareSignatureImpl(
  247. const ::NYql::NUdf::TStringRef& name,
  248. TDataTypeId typeId,
  249. ::NYql::NUdf::TType* userType,
  250. ::NYql::NUdf::IFunctionTypeInfoBuilder& builder,
  251. bool typesOnly)
  252. {
  253. if (DeclareSignatureImpl<TUserType>(name, typeId, userType, builder, typesOnly)) {
  254. return true;
  255. }
  256. return DeclareSignatureImpl<THead, TTail...>(name, typeId, userType, builder, typesOnly);
  257. }
  258. static bool DeclareSignature(
  259. const ::NYql::NUdf::TStringRef& name,
  260. ::NYql::NUdf::TType* userType,
  261. ::NYql::NUdf::IFunctionTypeInfoBuilder& builder,
  262. bool typesOnly)
  263. {
  264. if (Name() != name) {
  265. // the only case when we return false
  266. return false;
  267. }
  268. if (!userType) {
  269. builder.SetError("User type is not specified");
  270. return true;
  271. }
  272. auto typeHelper = builder.TypeInfoHelper();
  273. auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
  274. if (!userTypeInspector || userTypeInspector.GetElementsCount() < 1) {
  275. builder.SetError("Missing or invalid user type");
  276. return true;
  277. }
  278. auto argsTypeInspector = TTupleTypeInspector(*typeHelper, userTypeInspector.GetElementType(0));
  279. if (!argsTypeInspector || argsTypeInspector.GetElementsCount() < 1) {
  280. builder.SetError("Missing or invalid user type arguments");
  281. return true;
  282. }
  283. auto argType = ExtractArgFromUserType(argsTypeInspector.GetElementType(0), builder);
  284. TDataTypeInspector dataTypeInspector(*typeHelper, argType);
  285. if (!dataTypeInspector) {
  286. TStringStream ss;
  287. NUdf::TTypePrinter p(*typeHelper, argType);
  288. p.Out(ss);
  289. builder.SetError("User type must be a data type. Got: " + ss.Str());
  290. return true;
  291. }
  292. builder.UserType(userType);
  293. auto typeId = dataTypeInspector.GetTypeId();
  294. if (!DeclareSignatureImpl<TUserTypes...>(name, typeId, userType, builder, typesOnly)) {
  295. TStringBuilder sb;
  296. sb << "User type " << NYql::NUdf::GetDataTypeInfo(NYql::NUdf::GetDataSlot(typeId)).Name << " is not supported";
  297. builder.SetError(sb);
  298. }
  299. return true;
  300. }
  301. };
  302. template<typename... TUdfs>
  303. class TSimpleUdfModuleHelper : public IUdfModule
  304. {
  305. Y_HAS_SUBTYPE(TTypeAwareMarker);
  306. Y_HAS_SUBTYPE(TBlockType);
  307. public:
  308. void CleanupOnTerminate() const override {
  309. }
  310. template<typename TUdfType>
  311. void GetAllFunctionsImpl(IFunctionNamesSink& names) const {
  312. auto r = names.Add(TUdfType::Name());
  313. if (THasTTypeAwareMarker<TUdfType>::value) {
  314. r->SetTypeAwareness();
  315. }
  316. if constexpr (THasTBlockType<TUdfType>::value) {
  317. if constexpr (!std::is_same_v<typename TUdfType::TBlockType, void>) {
  318. auto rBlocks = names.Add(TUdfType::TBlockType::Name());
  319. rBlocks->SetTypeAwareness();
  320. }
  321. }
  322. }
  323. template<typename THead1, typename THead2, typename... TTail>
  324. void GetAllFunctionsImpl(IFunctionNamesSink& names) const {
  325. GetAllFunctionsImpl<THead1>(names);
  326. GetAllFunctionsImpl<THead2, TTail...>(names);
  327. }
  328. template<typename TUdfType>
  329. bool BuildFunctionTypeInfoImpl(
  330. const TStringRef& name,
  331. TType* userType,
  332. const TStringRef& typeConfig,
  333. ui32 flags,
  334. IFunctionTypeInfoBuilder& builder) const
  335. {
  336. Y_UNUSED(typeConfig);
  337. bool typesOnly = (flags & TFlags::TypesOnly);
  338. bool found = TUdfType::DeclareSignature(name, userType, builder, typesOnly);
  339. if (!found) {
  340. if constexpr (THasTBlockType<TUdfType>::value) {
  341. if constexpr (!std::is_same_v<typename TUdfType::TBlockType, void>) {
  342. found = TUdfType::TBlockType::DeclareSignature(name, userType, builder, typesOnly);
  343. }
  344. }
  345. }
  346. return found;
  347. }
  348. template<typename THead1, typename THead2, typename... TTail>
  349. bool BuildFunctionTypeInfoImpl(
  350. const TStringRef& name,
  351. TType* userType,
  352. const TStringRef& typeConfig,
  353. ui32 flags,
  354. IFunctionTypeInfoBuilder& builder) const
  355. {
  356. bool found = BuildFunctionTypeInfoImpl<THead1>(name, userType, typeConfig, flags, builder);
  357. if (!found) {
  358. found = BuildFunctionTypeInfoImpl<THead2, TTail...>(name, userType, typeConfig, flags, builder);
  359. }
  360. return found;
  361. }
  362. void GetAllFunctions(IFunctionsSink& sink) const final {
  363. GetAllFunctionsImpl<TUdfs...>(sink);
  364. }
  365. void BuildFunctionTypeInfo(
  366. const TStringRef& name,
  367. TType* userType,
  368. const TStringRef& typeConfig,
  369. ui32 flags,
  370. IFunctionTypeInfoBuilder& builder) const override
  371. {
  372. try {
  373. bool found = BuildFunctionTypeInfoImpl<TUdfs...>(name, userType, typeConfig, flags, builder);
  374. if (!found) {
  375. TStringBuilder sb;
  376. sb << "Unknown function: " << name.Data();
  377. builder.SetError(sb);
  378. }
  379. } catch (const std::exception&) {
  380. builder.SetError(CurrentExceptionMessage());
  381. }
  382. }
  383. };
  384. } // namspace NUdf
  385. } // namspace NYql