Skip to content

Commit c77fe32

Browse files
committed
YQL-20039: Make Datetime::Format work with basic resources for old MKQL versions
commit_hash:cc278d11897e8f36249bd3b285613b61c853d034 (cherry picked from commit 06674e6)
1 parent 602f669 commit c77fe32

File tree

2 files changed

+533
-24
lines changed

2 files changed

+533
-24
lines changed

yql/essentials/minikql/comp_nodes/mkql_udf.cpp

Lines changed: 162 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <yql/essentials/minikql/mkql_node_printer.h>
99
#include <yql/essentials/minikql/mkql_type_builder.h>
1010
#include <yql/essentials/minikql/mkql_utils.h>
11+
#include <yql/essentials/minikql/datetime/datetime64.h>
1112
#include <yql/essentials/utils/yql_panic.h>
1213

1314
#include <library/cpp/containers/stack_array/stack_array.h>
@@ -27,6 +28,46 @@ TString TruncateTypeDiff(const TString& s) {
2728
return s.substr(0,TypeDiffLimit) + "...";
2829
}
2930

31+
static const char TMResourceName[] = "DateTime2.TM";
32+
static const char TM64ResourceName[] = "DateTime2.TM64";
33+
// XXX: This class implements the wrapper to properly handle the
34+
// case when the signature of the emitted callable (i.e. callable
35+
// type) requires the extended datetime resource as an argument,
36+
// but the basic one is given. It wraps the unboxed value with
37+
// the closure to add the bridge with the implicit datetime
38+
// resource conversion.
39+
class TDateTimeConvertWrapper: public NUdf::TBoxedValue {
40+
public:
41+
TDateTimeConvertWrapper(NUdf::TUnboxedValue&& callable)
42+
: Callable_(callable)
43+
{};
44+
45+
private:
46+
NUdf::TUnboxedValue Run(const NUdf::IValueBuilder* valueBuilder, const NUdf::TUnboxedValuePod* args) const final {
47+
return NUdf::TUnboxedValuePod(new TDateTimeConverter(Callable_.Run(valueBuilder, args)));
48+
}
49+
50+
class TDateTimeConverter: public NUdf::TBoxedValue {
51+
public:
52+
TDateTimeConverter(NUdf::TUnboxedValue&& closure)
53+
: Closure_(closure)
54+
{}
55+
private:
56+
NUdf::TUnboxedValue Run(const NUdf::IValueBuilder* valueBuilder, const NUdf::TUnboxedValuePod* args) const final {
57+
NUdf::TUnboxedValuePod newArg;
58+
const auto arg = args[0];
59+
const auto& narrow = *reinterpret_cast<const NYql::DateTime::TTMStorage*>(arg.GetRawPtr());
60+
auto& extended = *reinterpret_cast<NYql::DateTime::TTM64Storage*>(newArg.GetRawPtr());
61+
extended.From(narrow);
62+
return Closure_.Run(valueBuilder, &newArg);
63+
}
64+
65+
const NUdf::TUnboxedValue Closure_;
66+
};
67+
68+
const NUdf::TUnboxedValue Callable_;
69+
};
70+
3071
template<class TValidatePolicy, class TValidateMode>
3172
class TSimpleUdfWrapper: public TMutableComputationNode<TSimpleUdfWrapper<TValidatePolicy,TValidateMode>> {
3273
using TBaseComputation = TMutableComputationNode<TSimpleUdfWrapper<TValidatePolicy,TValidateMode>>;
@@ -38,14 +79,16 @@ using TBaseComputation = TMutableComputationNode<TSimpleUdfWrapper<TValidatePoli
3879
NUdf::TSourcePosition pos,
3980
const TCallableType* callableType,
4081
const TCallableType* functionType,
41-
TType* userType)
82+
TType* userType,
83+
bool wrapDateTimeConvert)
4284
: TBaseComputation(mutables, EValueRepresentation::Boxed)
4385
, FunctionName(std::move(functionName))
4486
, TypeConfig(std::move(typeConfig))
4587
, Pos(pos)
4688
, CallableType(callableType)
4789
, FunctionType(functionType)
4890
, UserType(userType)
91+
, WrapDateTimeConvert(wrapDateTimeConvert)
4992
{
5093
this->Stateless = false;
5194
}
@@ -62,6 +105,7 @@ using TBaseComputation = TMutableComputationNode<TSimpleUdfWrapper<TValidatePoli
62105
NUdf::TUnboxedValue udf(NUdf::TUnboxedValuePod(funcInfo.Implementation.Release()));
63106
TValidate<TValidatePolicy,TValidateMode>::WrapCallable(FunctionType, udf, TStringBuilder() << "FunctionWithConfig<" << FunctionName << ">");
64107
ExtendArgs(udf, CallableType, funcInfo.FunctionType);
108+
ConvertDateTimeArg(udf);
65109
return udf.Release();
66110
}
67111
private:
@@ -102,6 +146,12 @@ using TBaseComputation = TMutableComputationNode<TSimpleUdfWrapper<TValidatePoli
102146
}
103147
}
104148

149+
void ConvertDateTimeArg(NUdf::TUnboxedValue& callable) const {
150+
if (WrapDateTimeConvert) {
151+
callable = NUdf::TUnboxedValuePod(new TDateTimeConvertWrapper(std::move(callable)));
152+
}
153+
}
154+
105155
void RegisterDependencies() const final {}
106156

107157
const TString FunctionName;
@@ -110,6 +160,7 @@ using TBaseComputation = TMutableComputationNode<TSimpleUdfWrapper<TValidatePoli
110160
const TCallableType *const CallableType;
111161
const TCallableType *const FunctionType;
112162
TType *const UserType;
163+
bool WrapDateTimeConvert;
113164
};
114165

115166
class TUdfRunCodegeneratorNode: public TSimpleUdfWrapper<TValidateErrorPolicyNone, TValidateModeLazy<TValidateErrorPolicyNone>>
@@ -126,11 +177,12 @@ class TUdfRunCodegeneratorNode: public TSimpleUdfWrapper<TValidateErrorPolicyNon
126177
const TCallableType* callableType,
127178
const TCallableType* functionType,
128179
TType* userType,
180+
bool wrapDateTimeConvert,
129181
TString&& moduleIRUniqID,
130182
TString&& moduleIR,
131183
TString&& fuctioNameIR,
132184
NUdf::TUniquePtr<NUdf::IBoxedValue>&& impl)
133-
: TSimpleUdfWrapper(mutables, std::move(functionName), std::move(typeConfig), pos, callableType, functionType, userType)
185+
: TSimpleUdfWrapper(mutables, std::move(functionName), std::move(typeConfig), pos, callableType, functionType, userType, wrapDateTimeConvert)
134186
, ModuleIRUniqID(std::move(moduleIRUniqID))
135187
, ModuleIR(std::move(moduleIR))
136188
, IRFunctionName(std::move(fuctioNameIR))
@@ -174,7 +226,8 @@ using TBaseComputation = TMutableCodegeneratorPtrNode<TUdfWrapper<TValidatePolic
174226
IComputationNode* runConfigNode,
175227
ui32 runConfigArgs,
176228
const TCallableType* callableType,
177-
TType* userType)
229+
TType* userType,
230+
bool wrapDateTimeConvert)
178231
: TBaseComputation(mutables, EValueRepresentation::Boxed)
179232
, FunctionName(std::move(functionName))
180233
, TypeConfig(std::move(typeConfig))
@@ -183,6 +236,7 @@ using TBaseComputation = TMutableCodegeneratorPtrNode<TUdfWrapper<TValidatePolic
183236
, RunConfigArgs(runConfigArgs)
184237
, CallableType(callableType)
185238
, UserType(userType)
239+
, WrapDateTimeConvert(wrapDateTimeConvert)
186240
, UdfIndex(mutables.CurValueIndex++)
187241
{
188242
this->Stateless = false;
@@ -193,6 +247,7 @@ using TBaseComputation = TMutableCodegeneratorPtrNode<TUdfWrapper<TValidatePolic
193247
if (!udf.HasValue()) {
194248
MakeUdf(ctx, udf);
195249
}
250+
ConvertDateTimeArg(udf);
196251
NStackArray::TStackArray<NUdf::TUnboxedValue> args(ALLOC_ON_STACK(NUdf::TUnboxedValue, RunConfigArgs));
197252
args[0] = RunConfigNode->GetValue(ctx);
198253
auto callable = udf.Run(ctx.Builder, args.data());
@@ -226,6 +281,11 @@ using TBaseComputation = TMutableCodegeneratorPtrNode<TUdfWrapper<TValidatePolic
226281

227282
block = main;
228283

284+
const auto convertFunc = ConstantInt::get(Type::getInt64Ty(context), GetMethodPtr(&TUdfWrapper::ConvertDateTimeArg));
285+
const auto convertType = FunctionType::get(Type::getVoidTy(context), {self->getType(), udfPtr->getType()}, false);
286+
const auto convertFuncPtr = CastInst::Create(Instruction::IntToPtr, convertFunc, PointerType::getUnqual(convertType), "convert", block);
287+
CallInst::Create(convertType, convertFuncPtr, {self, udfPtr}, "", block);
288+
229289
const auto argsType = ArrayType::get(valueType, RunConfigArgs);
230290
const auto args = new AllocaInst(argsType, 0U, "args", block);
231291
const auto zero = ConstantInt::get(indexType, 0);
@@ -270,6 +330,12 @@ using TBaseComputation = TMutableCodegeneratorPtrNode<TUdfWrapper<TValidatePolic
270330
TValidate<TValidatePolicy,TValidateMode>::WrapCallable(CallableType, callable, TStringBuilder() << "FunctionWithConfig<" << FunctionName << ">");
271331
}
272332

333+
void ConvertDateTimeArg(NUdf::TUnboxedValue& callable) const {
334+
if (WrapDateTimeConvert) {
335+
callable = NUdf::TUnboxedValuePod(new TDateTimeConvertWrapper(std::move(callable)));
336+
}
337+
}
338+
273339
void RegisterDependencies() const final {
274340
this->DependsOn(RunConfigNode);
275341
}
@@ -281,6 +347,7 @@ using TBaseComputation = TMutableCodegeneratorPtrNode<TUdfWrapper<TValidatePolic
281347
const ui32 RunConfigArgs;
282348
const TCallableType* CallableType;
283349
TType* const UserType;
350+
bool WrapDateTimeConvert;
284351
const ui32 UdfIndex;
285352
};
286353

@@ -310,6 +377,63 @@ inline IComputationNode* CreateUdfWrapper(const TComputationNodeFactoryContext&
310377
};
311378
}
312379

380+
// XXX: The helper below allows to make a stitchless upgrade
381+
// of MKQL runtime, regarding the incompatible changes made for
382+
// DateTime::Format UDF.
383+
template<bool Extended>
384+
static bool IsDateTimeResource(const TType* type) {
385+
if (!type->IsResource()) {
386+
return false;
387+
}
388+
const auto resourceName = AS_TYPE(TResourceType, type)->GetTag();
389+
390+
if constexpr (Extended) {
391+
return resourceName == NUdf::TStringRef::Of(TM64ResourceName);
392+
} else {
393+
return resourceName == NUdf::TStringRef::Of(TMResourceName);
394+
}
395+
}
396+
397+
static bool IsDateTimeConvertible(const NUdf::TStringRef& funcName,
398+
const TCallableType* nodeType,
399+
const TCallableType* funcType,
400+
bool& needConvert)
401+
{
402+
Y_DEBUG_ABORT_UNLESS(!needConvert);
403+
if (funcName == NUdf::TStringRef::Of("DateTime2.Format")) {
404+
Y_DEBUG_ABORT_UNLESS(nodeType->GetArgumentsCount());
405+
Y_DEBUG_ABORT_UNLESS(funcType->GetArgumentsCount());
406+
// XXX: In general, DateTime resources are not convertible
407+
// in runtime, but for the stitchless upgrade of MKQL
408+
// runtime, we consider the basic resource, being
409+
// convertible to the extended one for DateTime2.Format...
410+
if (IsDateTimeResource<false>(nodeType->GetArgumentType(0)) &&
411+
IsDateTimeResource<true>(funcType->GetArgumentType(0)))
412+
{
413+
// XXX: ... and to implicitly convert the basic resource
414+
// to the extended one, the closure has to be wrapped by
415+
// DateTimeConvertWrapper.
416+
needConvert = true;
417+
return true;
418+
}
419+
}
420+
if (funcName == NUdf::TStringRef::Of("DateTime2.Convert")) {
421+
// XXX: Vice versa convertion is forbidden as well, but
422+
// for the stitchless upgrade of MKQL runtime, we consider
423+
// the extended resource, being compatible with the basic
424+
// one for the return type of DateTime2.Convert.
425+
if (IsDateTimeResource<true>(nodeType->GetReturnType()) &&
426+
IsDateTimeResource<false>(funcType->GetReturnType()))
427+
{
428+
// XXX: However, DateTime2.Convert has to convert the
429+
// basic resource to the extended one.
430+
needConvert = false;
431+
return true;
432+
}
433+
}
434+
return false;
435+
}
436+
313437
}
314438

315439
IComputationNode* WrapUdf(TCallable& callable, const TComputationNodeFactoryContext& ctx) {
@@ -343,6 +467,7 @@ IComputationNode* WrapUdf(TCallable& callable, const TComputationNodeFactoryCont
343467

344468
MKQL_ENSURE(status.IsOk(), status.GetError());
345469

470+
bool wrapDateTimeConvert = false;
346471
const auto callableFuncType = AS_TYPE(TCallableType, funcInfo.FunctionType);
347472
const auto callableNodeType = AS_TYPE(TCallableType, callable.GetType()->GetReturnType());
348473
const auto runConfigFuncType = funcInfo.RunConfigType;
@@ -401,46 +526,59 @@ IComputationNode* WrapUdf(TCallable& callable, const TComputationNodeFactoryCont
401526
}
402527
const auto closureFuncType = runConfigNodeType->IsVoid()
403528
? callableFuncType
404-
: AS_TYPE(TCallableType, callableFuncType)->GetReturnType();
529+
: AS_TYPE(TCallableType, callableFuncType->GetReturnType());
405530
const auto closureNodeType = runConfigNodeType->IsVoid()
406-
? AS_TYPE(TCallableType, callableNodeType)->GetReturnType()
531+
? AS_TYPE(TCallableType, callableNodeType->GetReturnType())
407532
: callableNodeType;
408533
if (!closureNodeType->IsConvertableTo(*closureFuncType)) {
409-
TString diff = TStringBuilder()
410-
<< "type mismatch, expected return type: "
411-
<< PrintNode(closureNodeType, true)
412-
<< ", actual: "
413-
<< PrintNode(closureFuncType, true);
414-
UdfTerminate((TStringBuilder() << pos
415-
<< " Udf Function '"
416-
<< funcName
417-
<< "' "
418-
<< TruncateTypeDiff(diff)).c_str());
534+
if (!IsDateTimeConvertible(funcName, closureNodeType, closureFuncType, wrapDateTimeConvert)) {
535+
TString diff = TStringBuilder()
536+
<< "type mismatch, expected return type: "
537+
<< PrintNode(closureNodeType, true)
538+
<< ", actual: "
539+
<< PrintNode(closureFuncType, true);
540+
UdfTerminate((TStringBuilder() << pos
541+
<< " Udf Function '"
542+
<< funcName
543+
<< "' "
544+
<< TruncateTypeDiff(diff)).c_str());
545+
}
546+
MKQL_ENSURE(funcName == NUdf::TStringRef::Of("DateTime2.Format") ||
547+
funcName == NUdf::TStringRef::Of("DateTime2.Convert"),
548+
"Unexpected function violates the convertible invariants");
419549
}
420550

421551
const auto runConfigCompNode = LocateNode(ctx.NodeLocator, *runCfgNode.GetNode());
422552
const auto runConfigArgs = funcInfo.FunctionType->GetArgumentsCount();
423553
return runConfigNodeType->IsVoid()
424-
? CreateUdfWrapper<true>(ctx, std::move(funcName), std::move(typeConfig), pos, callableNodeType, callableFuncType, userType)
425-
: CreateUdfWrapper<false>(ctx, std::move(funcName), std::move(typeConfig), pos, runConfigCompNode, runConfigArgs, callableNodeType, userType);
554+
? CreateUdfWrapper<true>(ctx, std::move(funcName), std::move(typeConfig), pos, callableNodeType, callableFuncType, userType, wrapDateTimeConvert)
555+
: CreateUdfWrapper<false>(ctx, std::move(funcName), std::move(typeConfig), pos, runConfigCompNode, runConfigArgs, callableNodeType, userType, wrapDateTimeConvert);
556+
}
557+
558+
if (!callableFuncType->IsConvertableTo(*callableNodeType, true)) {
559+
if (!IsDateTimeConvertible(funcName, callableNodeType, callableFuncType, wrapDateTimeConvert)) {
560+
TString diff = TStringBuilder() << "type mismatch, expected return type: " << PrintNode(callableNodeType, true) <<
561+
", actual:" << PrintNode(callableFuncType, true);
562+
UdfTerminate((TStringBuilder() << pos << " UDF Function '" << funcName << "' " << TruncateTypeDiff(diff)).c_str());
563+
}
564+
MKQL_ENSURE(funcName == NUdf::TStringRef::Of("DateTime2.Format") ||
565+
funcName == NUdf::TStringRef::Of("DateTime2.Convert"),
566+
"Unexpected function violates the convertible invariants");
426567
}
427-
MKQL_ENSURE(callableFuncType->IsConvertableTo(*callableNodeType, true),
428-
"Function '" << funcName << "' type mismatch, expected return type: " << PrintNode(callableNodeType, true) <<
429-
", actual:" << PrintNode(callableFuncType, true));
430568
MKQL_ENSURE(funcInfo.Implementation, "UDF implementation is not set for function " << funcName);
431569

432570
if (runConfigFuncType->IsVoid()) {
433571
if (ctx.ValidateMode == NUdf::EValidateMode::None && funcInfo.ModuleIR && funcInfo.IRFunctionName) {
434572
return new TUdfRunCodegeneratorNode(
435-
ctx.Mutables, std::move(funcName), std::move(typeConfig), pos, callableNodeType, callableFuncType, userType,
573+
ctx.Mutables, std::move(funcName), std::move(typeConfig), pos, callableNodeType, callableFuncType, userType, wrapDateTimeConvert,
436574
std::move(funcInfo.ModuleIRUniqID), std::move(funcInfo.ModuleIR), std::move(funcInfo.IRFunctionName), std::move(funcInfo.Implementation)
437575
);
438576
}
439-
return CreateUdfWrapper<true>(ctx, std::move(funcName), std::move(typeConfig), pos, callableNodeType, callableFuncType, userType);
577+
return CreateUdfWrapper<true>(ctx, std::move(funcName), std::move(typeConfig), pos, callableNodeType, callableFuncType, userType, wrapDateTimeConvert);
440578
}
441579

442580
const auto runCfgCompNode = LocateNode(ctx.NodeLocator, *runCfgNode.GetNode());
443-
return CreateUdfWrapper<false>(ctx, std::move(funcName), std::move(typeConfig), pos, runCfgCompNode, 1U, callableNodeType, userType);
581+
return CreateUdfWrapper<false>(ctx, std::move(funcName), std::move(typeConfig), pos, runCfgCompNode, 1U, callableNodeType, userType, wrapDateTimeConvert);
444582
}
445583

446584
IComputationNode* WrapScriptUdf(TCallable& callable, const TComputationNodeFactoryContext& ctx) {
@@ -479,7 +617,7 @@ IComputationNode* WrapScriptUdf(TCallable& callable, const TComputationNodeFacto
479617
const auto funcTypeInfo = static_cast<TCallableType*>(callableResultType);
480618

481619
const auto programCompNode = LocateNode(ctx.NodeLocator, *programNode.GetNode());
482-
return CreateUdfWrapper<false>(ctx, std::move(funcName), std::move(typeConfig), pos, programCompNode, 1U, funcTypeInfo, userType);
620+
return CreateUdfWrapper<false>(ctx, std::move(funcName), std::move(typeConfig), pos, programCompNode, 1U, funcTypeInfo, userType, false);
483621
}
484622

485623
}

0 commit comments

Comments
 (0)