mkql_tostring.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #include "mkql_tostring.h"
  2. #include <yql/essentials/minikql/computation/mkql_computation_node_holders.h>
  3. #include <yql/essentials/minikql/computation/mkql_computation_node_codegen.h> // Y_IGNORE
  4. #include <yql/essentials/minikql/mkql_node_cast.h>
  5. #include <yql/essentials/minikql/mkql_node_builder.h>
  6. #include <yql/essentials/minikql/mkql_type_ops.h>
  7. #include <yql/essentials/minikql/mkql_string_util.h>
  8. #include <yql/essentials/public/decimal/yql_decimal.h>
  9. #include <yql/essentials/public/udf/udf_terminator.h>
  10. #ifndef MKQL_DISABLE_CODEGEN
  11. Y_PRAGMA_DIAGNOSTIC_PUSH
  12. Y_PRAGMA("GCC diagnostic ignored \"-Wreturn-type-c-linkage\"")
  13. extern "C" NYql::NUdf::TUnboxedValuePod DataToString(NYql::NUdf::TUnboxedValuePod data, NYql::NUdf::EDataSlot slot) {
  14. return NKikimr::NMiniKQL::ValueToString(slot, data);
  15. }
  16. extern "C" NYql::NUdf::TUnboxedValuePod DecimalToString(NYql::NDecimal::TInt128 decimal, ui8 precision, ui8 scale) {
  17. if (const auto str = NYql::NDecimal::ToString(decimal, precision, scale)) {
  18. return NKikimr::NMiniKQL::MakeString(NYql::NUdf::TStringRef(str, std::strlen(str)));
  19. }
  20. return NYql::NUdf::TUnboxedValuePod();
  21. }
  22. Y_PRAGMA_DIAGNOSTIC_POP
  23. #endif
  24. namespace NKikimr {
  25. namespace NMiniKQL {
  26. namespace {
  27. template <bool IsOptional>
  28. class TDecimalToStringWrapper : public TMutableCodegeneratorNode<TDecimalToStringWrapper<IsOptional>> {
  29. typedef TMutableCodegeneratorNode<TDecimalToStringWrapper<IsOptional>> TBaseComputation;
  30. public:
  31. TDecimalToStringWrapper(TComputationMutables& mutables, IComputationNode* data, ui8 precision, ui8 scale)
  32. : TBaseComputation(mutables, EValueRepresentation::String)
  33. , Data(data)
  34. , Precision(precision)
  35. , Scale(scale)
  36. {
  37. MKQL_ENSURE(precision > 0 && precision <= NYql::NDecimal::MaxPrecision, "Wrong precision.");
  38. MKQL_ENSURE(scale <= precision, "Wrong scale.");
  39. }
  40. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  41. const auto& dataValue = Data->GetValue(ctx);
  42. if (IsOptional && !dataValue) {
  43. return NUdf::TUnboxedValuePod();
  44. }
  45. if (const auto str = NYql::NDecimal::ToString(dataValue.GetInt128(), Precision, Scale)) {
  46. return MakeString(NUdf::TStringRef(str, std::strlen(str)));
  47. }
  48. Throw();
  49. }
  50. #ifndef MKQL_DISABLE_CODEGEN
  51. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  52. auto& context = ctx.Codegen.GetContext();
  53. const auto valType = Type::getInt128Ty(context);
  54. const auto psType = Type::getInt8Ty(context);
  55. const auto name = "DecimalToString";
  56. ctx.Codegen.AddGlobalMapping(name, reinterpret_cast<const void*>(&DecimalToString));
  57. const auto fnType =
  58. FunctionType::get(valType, { valType, psType, psType }, false);
  59. const auto func = ctx.Codegen.GetModule().getOrInsertFunction(name, fnType);
  60. const auto fail = BasicBlock::Create(context, "fail", ctx.Func);
  61. const auto nice = BasicBlock::Create(context, "nice", ctx.Func);
  62. const auto zero = ConstantInt::get(valType, 0ULL);
  63. const auto precision = ConstantInt::get(psType, Precision);
  64. const auto scale = ConstantInt::get(psType, Scale);
  65. const auto value = GetNodeValue(Data, ctx, block);
  66. Value* result;
  67. if constexpr (IsOptional) {
  68. const auto call = BasicBlock::Create(context, "call", ctx.Func);
  69. const auto res = PHINode::Create(valType, 2, "result", nice);
  70. res->addIncoming(zero, block);
  71. result = res;
  72. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, value, zero, "check", block);
  73. BranchInst::Create(nice, call, check, block);
  74. block = call;
  75. const auto string = CallInst::Create(func, { GetterForInt128(value, block), precision, scale }, "to_string", block);
  76. res->addIncoming(string, block);
  77. const auto test = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, string, zero, "test", block);
  78. BranchInst::Create(fail, nice, test, block);
  79. } else {
  80. result = CallInst::Create(func, { GetterForInt128(value, block), precision, scale }, "to_string", block);
  81. const auto test = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, result, zero, "test", block);
  82. BranchInst::Create(fail, nice, test, block);
  83. }
  84. block = fail;
  85. const auto doFunc = ConstantInt::get(Type::getInt64Ty(context), GetMethodPtr(&TDecimalToStringWrapper::Throw));
  86. const auto doFuncType = FunctionType::get(Type::getVoidTy(context), {}, false);
  87. const auto doFuncPtr = CastInst::Create(Instruction::IntToPtr, doFunc, PointerType::getUnqual(doFuncType), "thrower", block);
  88. CallInst::Create(doFuncType, doFuncPtr, {}, "", block);
  89. new UnreachableInst(context, block);
  90. block = nice;
  91. return result;
  92. }
  93. #endif
  94. private:
  95. void RegisterDependencies() const final {
  96. this->DependsOn(Data);
  97. }
  98. [[noreturn]] static void Throw() {
  99. UdfTerminate("Ivalid Decimal value.");
  100. }
  101. IComputationNode* const Data;
  102. const ui8 Precision, Scale;
  103. };
  104. template <bool IsOptional>
  105. class TToStringWrapper : public TMutableCodegeneratorNode<TToStringWrapper<IsOptional>> {
  106. typedef TMutableCodegeneratorNode<TToStringWrapper<IsOptional>> TBaseComputation;
  107. public:
  108. TToStringWrapper(TComputationMutables& mutables, IComputationNode* data, NUdf::TDataTypeId schemeType)
  109. : TBaseComputation(mutables, EValueRepresentation::String)
  110. , Data(data)
  111. , SchemeType(NUdf::GetDataSlot(schemeType))
  112. {}
  113. NUdf::TUnboxedValue DoCalculate(TComputationContext& ctx) const {
  114. const auto& dataValue = Data->GetValue(ctx);
  115. if (IsOptional && !dataValue) {
  116. return NUdf::TUnboxedValuePod();
  117. }
  118. return ValueToString(SchemeType, dataValue);
  119. }
  120. #ifndef MKQL_DISABLE_CODEGEN
  121. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  122. auto& context = ctx.Codegen.GetContext();
  123. const auto valType = Type::getInt128Ty(context);
  124. const auto slotType = Type::getInt32Ty(context);
  125. const auto name = "DataToString";
  126. ctx.Codegen.AddGlobalMapping(name, reinterpret_cast<const void*>(&DataToString));
  127. const auto fnType =
  128. FunctionType::get(valType, { valType, slotType }, false);
  129. const auto func = ctx.Codegen.GetModule().getOrInsertFunction(name, fnType);
  130. const auto zero = ConstantInt::get(valType, 0ULL);
  131. const auto slot = ConstantInt::get(slotType, static_cast<ui32>(SchemeType));
  132. const auto value = GetNodeValue(Data, ctx, block);
  133. if constexpr (IsOptional) {
  134. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  135. const auto call = BasicBlock::Create(context, "call", ctx.Func);
  136. const auto result = PHINode::Create(valType, 2, "result", done);
  137. result->addIncoming(zero, block);
  138. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, value, zero, "check", block);
  139. BranchInst::Create(done, call, check, block);
  140. block = call;
  141. const auto string = CallInst::Create(func, { value, slot }, "to_string", block);
  142. if (Data->IsTemporaryValue())
  143. ValueCleanup(Data->GetRepresentation(), value, ctx, block);
  144. result->addIncoming(string, block);
  145. BranchInst::Create(done, block);
  146. block = done;
  147. return result;
  148. } else {
  149. const auto string = CallInst::Create(func, { value, slot }, "to_string", block);
  150. if (Data->IsTemporaryValue())
  151. ValueCleanup(Data->GetRepresentation(), value, ctx, block);
  152. return string;
  153. }
  154. }
  155. #endif
  156. private:
  157. void RegisterDependencies() const final {
  158. this->DependsOn(Data);
  159. }
  160. IComputationNode* const Data;
  161. const NUdf::EDataSlot SchemeType;
  162. };
  163. class TAsIsWrapper : public TDecoratorCodegeneratorNode<TAsIsWrapper> {
  164. using TBaseComputation = TDecoratorCodegeneratorNode<TAsIsWrapper>;
  165. public:
  166. TAsIsWrapper(IComputationNode* optional)
  167. : TBaseComputation(optional)
  168. {}
  169. NUdf::TUnboxedValuePod DoCalculate(TComputationContext&, const NUdf::TUnboxedValuePod& value) const { return value; }
  170. #ifndef MKQL_DISABLE_CODEGEN
  171. Value* DoGenerateGetValue(const TCodegenContext&, Value* value, BasicBlock*&) const { return value; }
  172. #endif
  173. };
  174. }
  175. IComputationNode* WrapToString(TCallable& callable, const TComputationNodeFactoryContext& ctx) {
  176. MKQL_ENSURE(callable.GetInputsCount() == 1, "Expected 1 arg");
  177. bool isOptional;
  178. const auto dataType = UnpackOptionalData(callable.GetInput(0), isOptional);
  179. const auto schemeType = dataType->GetSchemeType();
  180. const auto data = LocateNode(ctx.NodeLocator, callable, 0);
  181. if (NUdf::EDataTypeFeatures::StringType & NUdf::GetDataTypeInfo(NUdf::GetDataSlot(schemeType)).Features) {
  182. return new TAsIsWrapper(data);
  183. } else if (NUdf::TDataType<NUdf::TDecimal>::Id == schemeType) {
  184. const auto& params = static_cast<TDataDecimalType*>(dataType)->GetParams();
  185. if (isOptional) {
  186. return new TDecimalToStringWrapper<true>(ctx.Mutables, data, params.first, params.second);
  187. } else {
  188. return new TDecimalToStringWrapper<false>(ctx.Mutables, data, params.first, params.second);
  189. }
  190. } else {
  191. if (isOptional) {
  192. return new TToStringWrapper<true>(ctx.Mutables, data, schemeType);
  193. } else {
  194. return new TToStringWrapper<false>(ctx.Mutables, data, schemeType);
  195. }
  196. }
  197. }
  198. }
  199. }