summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ydb/library/yql/sql/v1/builtin.cpp2
-rw-r--r--ydb/library/yql/udfs/common/datetime2/datetime_udf.cpp196
-rw-r--r--ydb/library/yql/udfs/common/datetime2/test/canondata/result.json5
-rw-r--r--ydb/library/yql/udfs/common/datetime2/test/canondata/test.test_FormatMicroseconds_/results.txt98
-rw-r--r--ydb/library/yql/udfs/common/datetime2/test/cases/FormatMicroseconds.sql15
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)
+;