diff options
5 files changed, 229 insertions, 87 deletions
diff --git a/ydb/library/yql/sql/v1/builtin.cpp b/ydb/library/yql/sql/v1/builtin.cpp index 172fecab031..f4dacaa9c24 100644 --- a/ydb/library/yql/sql/v1/builtin.cpp +++ b/ydb/library/yql/sql/v1/builtin.cpp @@ -3420,7 +3420,7 @@ TNodePtr BuildBuiltinFunc(TContext& ctx, TPosition pos, TString name, const TVec return BuildUdf(ctx, pos, moduleName, name, newArgs); } - } else if (ns == "datetime2" && (name == "Format" || name == "Parse")) { + } else if (ns == "datetime2" && (name == "Parse")) { return BuildUdf(ctx, pos, nameSpace, name, args); } else if (ns == "pg") { const bool isAggregateFunc = NYql::NPg::HasAggregation(name, NYql::NPg::EAggKind::Normal); diff --git a/ydb/library/yql/udfs/common/datetime2/datetime_udf.cpp b/ydb/library/yql/udfs/common/datetime2/datetime_udf.cpp index 7de6b9bd074..d8d3ddd5162 100644 --- a/ydb/library/yql/udfs/common/datetime2/datetime_udf.cpp +++ b/ydb/library/yql/udfs/common/datetime2/datetime_udf.cpp @@ -1661,20 +1661,9 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build class TFormat : public TBoxedValue { public: - class TFactory : public TBoxedValue { - public: - explicit TFactory(TSourcePosition pos) - : Pos_(pos) - {} - - private: - TUnboxedValue Run(const IValueBuilder*, const TUnboxedValuePod* args) const final try { - return TUnboxedValuePod(new TFormat(args[0], Pos_)); - } catch (const std::exception& e) { - UdfTerminate((TStringBuilder() << Pos_ << " " << e.what()).data()); - } - const TSourcePosition Pos_; - }; + explicit TFormat(TSourcePosition pos) + : Pos_(pos) + {} static const TStringRef& Name() { static auto name = TStringRef::Of("Format"); @@ -1692,23 +1681,32 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build } auto resourceType = builder.Resource(TMResourceName); + auto optionalResourceType = builder.Optional()->Item(resourceType).Build(); + + auto stringType = builder.SimpleType<char*>(); + auto optionalStringType = builder.Optional()->Item(stringType).Build(); + + auto boolType = builder.SimpleType<bool>(); + auto optionalBoolType = builder.Optional()->Item(boolType).Build(); - builder.Args()->Add(resourceType).Flags(ICallablePayload::TArgumentFlags::AutoMap); - builder.RunConfig<char*>().Returns<char*>(); + auto args = builder.Args(); + args->Add(stringType); + args->Add(optionalBoolType).Name("AlwaysWriteFractionalSeconds"); + args->Done(); + builder.OptionalArgs(1); + builder.Returns(builder.Callable(1)->Arg(optionalResourceType).Returns(optionalStringType).Build()); if (!typesOnly) { - builder.Implementation(new TFormat::TFactory(builder.GetSourcePosition())); + builder.Implementation(new TFormat(builder.GetSourcePosition())); } + builder.IsStrict(); + return true; } private: - const TSourcePosition Pos_; - const TUnboxedValue Format_; - std::vector<std::function<size_t(char*, const TUnboxedValuePod&, const IDateBuilder&)>> Printers_; - - size_t ReservedSize_; + using TPrintersList = std::vector<std::function<size_t(char*, const TUnboxedValuePod&, const IDateBuilder&)>>; struct TDataPrinter { const std::string_view Data; @@ -1719,46 +1717,17 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build } }; - private: - TUnboxedValue Run( - const IValueBuilder* valueBuilder, - const TUnboxedValuePod* args) const override - { - try { - EMPTY_RESULT_ON_EMPTY_ARG(0); - const auto value = args[0]; - - auto& builder = valueBuilder->GetDateBuilder(); - - auto result = valueBuilder->NewStringNotFilled(ReservedSize_); - auto pos = result.AsStringRef().Data(); - ui32 size = 0U; - - for (const auto& printer : Printers_) { - if (const auto plus = printer(pos, value, builder)) { - size += plus; - pos += plus; - } - } - - if (size < ReservedSize_) { - result = valueBuilder->SubString(result.Release(), 0U, size); - } - - return result; - } catch (const std::exception& e) { - UdfTerminate((TStringBuilder() << Pos_ << " " << e.what()).data()); + TUnboxedValue Run(const IValueBuilder*, const TUnboxedValuePod* args) const final try { + bool alwaysWriteFractionalSeconds = false; + if (auto val = args[1]) { + alwaysWriteFractionalSeconds = val.Get<bool>(); } - } - TFormat(const TUnboxedValuePod& runConfig, TSourcePosition pos) - : Pos_(pos) - , Format_(runConfig) - { - const std::string_view formatView(Format_.AsStringRef()); + const std::string_view formatView(args[0].AsStringRef()); auto dataStart = formatView.begin(); size_t dataSize = 0U; - ReservedSize_ = 0U; + size_t reservedSize = 0U; + TPrintersList printers; for (auto ptr = formatView.begin(); formatView.end() != ptr; ++ptr) { if (*ptr != '%') { @@ -1767,8 +1736,8 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build } if (dataSize) { - Printers_.emplace_back(TDataPrinter{std::string_view(&*dataStart, dataSize)}); - ReservedSize_ += dataSize; + printers.emplace_back(TDataPrinter{std::string_view(&*dataStart, dataSize)}); + reservedSize += dataSize; dataSize = 0U; } @@ -1779,70 +1748,73 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build switch (*ptr) { case '%': { static constexpr size_t size = 1; - Printers_.emplace_back([](char* out, const TUnboxedValuePod&, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod&, const IDateBuilder&) { *out = '%'; return size; }); - ReservedSize_ += size; + reservedSize += size; break; } case 'Y': { static constexpr size_t size = 4; - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { return PrintNDigits<size>::Do(GetYear(value), out); }); - ReservedSize_ += size; + reservedSize += size; break; } case 'm': { static constexpr size_t size = 2; - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { return PrintNDigits<size>::Do(GetMonth(value), out); }); - ReservedSize_ += size; + reservedSize += size; break; } case 'd': { static constexpr size_t size = 2; - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { return PrintNDigits<size>::Do(GetDay(value), out); }); - ReservedSize_ += size; + reservedSize += size; break; } case 'H': { static constexpr size_t size = 2; - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { return PrintNDigits<size>::Do(GetHour(value), out); }); - ReservedSize_ += size; + reservedSize += size; break; } case 'M': { static constexpr size_t size = 2; - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { return PrintNDigits<size>::Do(GetMinute(value), out); }); - ReservedSize_ += size; + reservedSize += size; break; } case 'S': - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([alwaysWriteFractionalSeconds](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { constexpr size_t size = 2; - if (const auto microsecond = GetMicrosecond(value)) { + if (const auto microsecond = GetMicrosecond(value); microsecond || alwaysWriteFractionalSeconds) { out += PrintNDigits<size>::Do(GetSecond(value), out); *out++ = '.'; constexpr size_t msize = 6; - return size + 1U + PrintNDigits<msize, false>::Do(microsecond, out); + auto addSz = alwaysWriteFractionalSeconds ? + PrintNDigits<msize, true>::Do(microsecond, out) : + PrintNDigits<msize, false>::Do(microsecond, out); + return size + 1U + addSz; } return PrintNDigits<size>::Do(GetSecond(value), out); }); - ReservedSize_ += 9; + reservedSize += 9; break; case 'z': { static constexpr size_t size = 5; - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder& builder) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder& builder) { auto timezoneId = GetTimezoneId(value); if (TTMStorage::IsUniversal(timezoneId)) { std::memcpy(out, "+0000", size); @@ -1862,21 +1834,21 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build out += PrintNDigits<2U>::Do(shift % 60U, out); return size; }); - ReservedSize_ += size; + reservedSize += size; break; } case 'Z': - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { const auto timezoneId = GetTimezoneId(value); const auto tzName = NUdf::GetTimezones()[timezoneId]; std::memcpy(out, tzName.data(), std::min(tzName.size(), MAX_TIMEZONE_NAME_LEN)); return tzName.size(); }); - ReservedSize_ += MAX_TIMEZONE_NAME_LEN; + reservedSize += MAX_TIMEZONE_NAME_LEN; break; case 'b': { static constexpr size_t size = 3; - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { static constexpr std::string_view mp[] { "Jan", "Feb", @@ -1896,11 +1868,11 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build std::memcpy(out, mp[month - 1].data(), size); return size; }); - ReservedSize_ += size; + reservedSize += size; break; } case 'B': { - Printers_.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { + printers.emplace_back([](char* out, const TUnboxedValuePod& value, const IDateBuilder&) { static constexpr std::string_view mp[] { "January", "February", @@ -1921,7 +1893,7 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build std::memcpy(out, monthFullName.data(), monthFullName.size()); return monthFullName.size(); }); - ReservedSize_ += 9U; // MAX_MONTH_FULL_NAME_LEN + reservedSize += 9U; // MAX_MONTH_FULL_NAME_LEN break; } default: @@ -1932,10 +1904,62 @@ TValue DoAddYears(const TValue& date, i64 years, const NUdf::IDateBuilder& build } if (dataSize) { - Printers_.emplace_back(TDataPrinter{std::string_view(dataStart, dataSize)}); - ReservedSize_ += dataSize; + printers.emplace_back(TDataPrinter{std::string_view(dataStart, dataSize)}); + reservedSize += dataSize; } + + return TUnboxedValuePod(new TImpl(Pos_, std::move(printers), reservedSize)); + } catch (const std::exception& e) { + UdfTerminate((TStringBuilder() << Pos_ << " " << e.what()).data()); } + + class TImpl : public TBoxedValue { + public: + TUnboxedValue Run( + const IValueBuilder* valueBuilder, + const TUnboxedValuePod* args) const override + { + try { + EMPTY_RESULT_ON_EMPTY_ARG(0); + const auto value = args[0]; + + auto& builder = valueBuilder->GetDateBuilder(); + + auto result = valueBuilder->NewStringNotFilled(ReservedSize_); + auto pos = result.AsStringRef().Data(); + ui32 size = 0U; + + for (const auto& printer : Printers_) { + if (const auto plus = printer(pos, value, builder)) { + size += plus; + pos += plus; + } + } + + if (size < ReservedSize_) { + result = valueBuilder->SubString(result.Release(), 0U, size); + } + + return result; + } catch (const std::exception& e) { + UdfTerminate((TStringBuilder() << Pos_ << " " << e.what()).data()); + } + } + + TImpl(TSourcePosition pos, TPrintersList&& printers, size_t reservedSize) + : Pos_(pos) + , Printers_(printers) + , ReservedSize_(reservedSize) + {} + + private: + const TSourcePosition Pos_; + + TPrintersList Printers_; + size_t ReservedSize_; + }; + + const TSourcePosition Pos_; }; template<size_t Digits> diff --git a/ydb/library/yql/udfs/common/datetime2/test/canondata/result.json b/ydb/library/yql/udfs/common/datetime2/test/canondata/result.json index 90b87abc918..6e475365ea6 100644 --- a/ydb/library/yql/udfs/common/datetime2/test/canondata/result.json +++ b/ydb/library/yql/udfs/common/datetime2/test/canondata/result.json @@ -34,6 +34,11 @@ "uri": "file://test.test_EndOf_/results.txt" } ], + "test.test[FormatMicroseconds]": [ + { + "uri": "file://test.test_FormatMicroseconds_/results.txt" + } + ], "test.test[Format]": [ { "uri": "file://test.test_Format_/results.txt" diff --git a/ydb/library/yql/udfs/common/datetime2/test/canondata/test.test_FormatMicroseconds_/results.txt b/ydb/library/yql/udfs/common/datetime2/test/canondata/test.test_FormatMicroseconds_/results.txt new file mode 100644 index 00000000000..5d1dfe80a94 --- /dev/null +++ b/ydb/library/yql/udfs/common/datetime2/test/canondata/test.test_FormatMicroseconds_/results.txt @@ -0,0 +1,98 @@ +[ + { + "Write" = [ + { + "Type" = [ + "ListType"; + [ + "StructType"; + [ + [ + "column0"; + [ + "OptionalType"; + [ + "DataType"; + "String" + ] + ] + ]; + [ + "column1"; + [ + "OptionalType"; + [ + "DataType"; + "String" + ] + ] + ]; + [ + "column2"; + [ + "OptionalType"; + [ + "DataType"; + "String" + ] + ] + ]; + [ + "column3"; + [ + "OptionalType"; + [ + "DataType"; + "String" + ] + ] + ]; + [ + "column4"; + [ + "OptionalType"; + [ + "DataType"; + "String" + ] + ] + ]; + [ + "column5"; + [ + "OptionalType"; + [ + "DataType"; + "String" + ] + ] + ] + ] + ] + ]; + "Data" = [ + [ + [ + "2024-01-01 00:00:00" + ]; + [ + "2024-01-01 00:00:00.000000" + ]; + [ + "2024-01-01 00:00:00.000001" + ]; + [ + "2024-01-01 00:00:00.000001" + ]; + [ + "2024-01-01 00:00:00.05" + ]; + [ + "2024-01-01 00:00:00.050000" + ] + ] + ] + } + ] + } +]
\ No newline at end of file diff --git a/ydb/library/yql/udfs/common/datetime2/test/cases/FormatMicroseconds.sql b/ydb/library/yql/udfs/common/datetime2/test/cases/FormatMicroseconds.sql new file mode 100644 index 00000000000..3517da3bf35 --- /dev/null +++ b/ydb/library/yql/udfs/common/datetime2/test/cases/FormatMicroseconds.sql @@ -0,0 +1,15 @@ +/* syntax version 1 */ +$parse = DateTime::Parse("%Y-%m-%d %H:%M:%S"); + +$dt0 = $parse("2024-01-01 00:00:00"); +$dt1 = $parse("2024-01-01 00:00:00.000001"); +$dt2 = $parse("2024-01-01 00:00:00.05"); + +$format = DateTime::Format("%Y-%m-%d %H:%M:%S"); +$format_ms = DateTime::Format("%Y-%m-%d %H:%M:%S", True as AlwaysWriteFractionalSeconds); + +SELECT + $format($dt0), $format_ms($dt0), + $format($dt1), $format_ms($dt1), + $format($dt2), $format_ms($dt2) +; |
