Skip to content
This repository was archived by the owner on May 9, 2024. It is now read-only.

Commit d14f208

Browse files
committed
[Time Conversion] Timestamp to time
Adding timestamp to time conversion. The units is also taken into account. Resolves: #318 Signed-off-by: Dmitrii Makarenko <[email protected]>
1 parent a031520 commit d14f208

File tree

9 files changed

+477
-80
lines changed

9 files changed

+477
-80
lines changed

omniscidb/IR/Expr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ bool isCastAllowed(const Type* old_type, const Type* new_type) {
217217
// can cast from date to timestamp
218218
} else if (old_type->isDate() && new_type->isTimestamp()) {
219219
return true;
220-
} else if (old_type->isTimestamp() && new_type->isDate()) {
220+
} else if (old_type->isTimestamp() && (new_type->isDate() || new_type->isTime())) {
221+
return true;
222+
} else if (old_type->isTime() && new_type->isInteger()) {
221223
return true;
222224
} else if (old_type->isBoolean() && new_type->isNumber()) {
223225
return true;

omniscidb/QueryBuilder/QueryBuilder.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -915,11 +915,11 @@ BuilderExpr BuilderExpr::cast(const Type* new_type) const {
915915
return {builder_, expr_->cast(new_type), "", true};
916916
}
917917
} else if (expr_->type()->isTime()) {
918-
if (new_type->isTime()) {
918+
if (new_type->isInteger() || new_type->isTime()) {
919919
return {builder_, expr_->cast(new_type), "", true};
920920
}
921921
} else if (expr_->type()->isTimestamp()) {
922-
if (new_type->isNumber() || new_type->isDate() || new_type->isTimestamp()) {
922+
if (new_type->isInteger() || new_type->isDateTime()) {
923923
return {builder_, expr_->cast(new_type), "", true};
924924
}
925925
}

omniscidb/QueryEngine/ArrowResultSetConverter.cpp

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -967,8 +967,19 @@ void append_scalar_value_and_validity(const ScalarTargetValue& value,
967967
}
968968
break;
969969
case hdk::ir::Type::kTime:
970-
create_or_append_value<int32_t, int64_t>(value, values, max_size);
971-
create_or_append_validity<int64_t>(value, type, null_bitmap, max_size);
970+
switch (type->size()) {
971+
case 4:
972+
create_or_append_value<int32_t, int64_t>(value, values, max_size);
973+
create_or_append_validity<int64_t>(value, type, null_bitmap, max_size);
974+
break;
975+
case 8:
976+
create_or_append_value<int64_t, int64_t>(value, values, max_size);
977+
create_or_append_validity<int64_t>(value, type, null_bitmap, max_size);
978+
break;
979+
default:
980+
throw std::runtime_error(type->toString() +
981+
" is not supported in Arrow result sets.");
982+
}
972983
break;
973984
case hdk::ir::Type::kDate:
974985
device_type == ExecutorDeviceType::GPU
@@ -1566,8 +1577,22 @@ std::shared_ptr<arrow::DataType> get_arrow_type(const hdk::ir::Type* type,
15661577
CHECK_EQ(type->size(), 8);
15671578
return arrow::decimal(std::min(type->as<hdk::ir::DecimalType>()->precision(), 38),
15681579
type->as<hdk::ir::DecimalType>()->scale());
1569-
case hdk::ir::Type::kTime:
1570-
return time32(arrow::TimeUnit::SECOND);
1580+
case hdk::ir::Type::kTime: {
1581+
auto unit = type->as<hdk::ir::TimeType>()->unit();
1582+
switch (unit) {
1583+
case hdk::ir::TimeUnit::kSecond:
1584+
return time32(arrow::TimeUnit::SECOND);
1585+
case hdk::ir::TimeUnit::kMilli:
1586+
return time32(arrow::TimeUnit::MILLI);
1587+
case hdk::ir::TimeUnit::kMicro:
1588+
return time64(arrow::TimeUnit::MICRO);
1589+
case hdk::ir::TimeUnit::kNano:
1590+
return time64(arrow::TimeUnit::NANO);
1591+
default:
1592+
throw std::runtime_error("Unsupported time precision for Arrow result sets: " +
1593+
toString(unit));
1594+
}
1595+
}
15711596
case hdk::ir::Type::kDate: {
15721597
// TODO(wamsi) : Remove date64() once date32() support is added in cuDF. date32()
15731598
// Currently support for date32() is missing in cuDF.Hence, if client requests for
@@ -1839,6 +1864,25 @@ void appendToColumnBuilder(ArrowResultSetConverter::ColumnBuilder& column_builde
18391864
}
18401865
}
18411866

1867+
template <>
1868+
void appendToColumnBuilder<arrow::Time32Builder, int64_t>(
1869+
ArrowResultSetConverter::ColumnBuilder& column_builder,
1870+
arrow::ArrayBuilder* arrow_builder,
1871+
const ValueArray& values,
1872+
const std::shared_ptr<std::vector<bool>>& is_valid) {
1873+
std::vector<int64_t> val_raw = boost::get<std::vector<int64_t>>(values);
1874+
std::vector<int32_t> vals(val_raw.begin(), val_raw.end());
1875+
1876+
auto typed_builder = dynamic_cast<arrow::Time32Builder*>(arrow_builder);
1877+
CHECK(typed_builder);
1878+
if (column_builder.field->nullable()) {
1879+
CHECK(is_valid.get());
1880+
ARROW_THROW_NOT_OK(typed_builder->AppendValues(vals, *is_valid));
1881+
} else {
1882+
ARROW_THROW_NOT_OK(typed_builder->AppendValues(vals));
1883+
}
1884+
}
1885+
18421886
template <>
18431887
void appendToColumnBuilder<arrow::Decimal128Builder, int64_t>(
18441888
ArrowResultSetConverter::ColumnBuilder& column_builder,
@@ -2005,10 +2049,25 @@ void ArrowResultSetConverter::append(
20052049
" is not supported in Arrow result sets.");
20062050
}
20072051
break;
2008-
case hdk::ir::Type::kTime:
2009-
appendToColumnBuilder<arrow::Time32Builder, int32_t>(
2010-
column_builder, elem_builder, values, is_valid);
2052+
case hdk::ir::Type::kTime: {
2053+
auto unit = elem_type->as<hdk::ir::TimeType>()->unit();
2054+
switch (unit) {
2055+
case hdk::ir::TimeUnit::kSecond:
2056+
case hdk::ir::TimeUnit::kMilli:
2057+
appendToColumnBuilder<arrow::Time32Builder, int64_t>(
2058+
column_builder, elem_builder, values, is_valid);
2059+
break;
2060+
case hdk::ir::TimeUnit::kMicro:
2061+
case hdk::ir::TimeUnit::kNano:
2062+
appendToColumnBuilder<arrow::Time64Builder, int64_t>(
2063+
column_builder, elem_builder, values, is_valid);
2064+
break;
2065+
default:
2066+
throw std::runtime_error("Unsupported time precision for Arrow result sets: " +
2067+
toString(unit));
2068+
}
20112069
break;
2070+
}
20122071
case hdk::ir::Type::kTimestamp:
20132072
appendToColumnBuilder<arrow::TimestampBuilder, int64_t>(
20142073
column_builder, elem_builder, values, is_valid);

omniscidb/QueryEngine/CastIR.cpp

Lines changed: 93 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -73,58 +73,58 @@ llvm::Value* CodeGenerator::codegenCast(llvm::Value* operand_lv,
7373
return cgen_state_->ir_builder_.CreatePointerCast(operand_lv,
7474
byte_array_type->getPointerTo());
7575
}
76-
if (operand_lv->getType()->isIntegerTy()) {
77-
if (operand_type->isString() || operand_type->isExtDictionary()) {
78-
return codegenCastFromString(
79-
operand_lv, operand_type, type, operand_is_const, is_dict_intersection, co);
80-
}
81-
CHECK(operand_type->isInteger() || operand_type->isDecimal() ||
82-
operand_type->isDateTime() || operand_type->isBoolean());
83-
if (operand_type->isBoolean()) {
84-
// cast boolean to int8
85-
CHECK(operand_lv->getType()->isIntegerTy(1) ||
86-
operand_lv->getType()->isIntegerTy(8));
87-
if (operand_lv->getType()->isIntegerTy(1)) {
88-
operand_lv = cgen_state_->castToTypeIn(operand_lv, 8);
89-
}
90-
if (type->isBoolean()) {
91-
return operand_lv;
92-
}
93-
}
94-
if (operand_type->isInteger() && operand_lv->getType()->isIntegerTy(8) &&
95-
type->isBoolean()) {
96-
// cast int8 to boolean
97-
return codegenCastBetweenIntTypes(operand_lv, operand_type, type);
98-
}
99-
if (operand_type->isTimestamp() && type->isDate()) {
100-
// Maybe we should instead generate DateTruncExpr directly from RelAlgTranslator
101-
// for this pattern. However, DateTruncExpr is supposed to return a timestamp,
102-
// whereas this cast returns a date. The underlying type for both is still the same,
103-
// but it still doesn't look like a good idea to misuse DateTruncExpr.
104-
// Date will have default precision of day, but TIMESTAMP dimension would
105-
// matter but while converting date through seconds
106-
return codegenCastTimestampToDate(
107-
operand_lv,
108-
operand_type->as<hdk::ir::TimestampType>()->unit(),
109-
type->nullable());
76+
if (!operand_lv->getType()->isIntegerTy()) {
77+
return codegenCastFromFp(operand_lv, operand_type, type);
78+
}
79+
80+
if (operand_type->isString() || operand_type->isExtDictionary()) {
81+
return codegenCastFromString(
82+
operand_lv, operand_type, type, operand_is_const, is_dict_intersection, co);
83+
}
84+
CHECK(operand_type->isInteger() || operand_type->isDecimal() ||
85+
operand_type->isDateTime() || operand_type->isBoolean());
86+
if (operand_type->isBoolean()) {
87+
// cast boolean to int8
88+
CHECK(operand_lv->getType()->isIntegerTy(1) || operand_lv->getType()->isIntegerTy(8));
89+
if (operand_lv->getType()->isIntegerTy(1)) {
90+
operand_lv = cgen_state_->castToTypeIn(operand_lv, 8);
11091
}
111-
if ((operand_type->isTimestamp() || operand_type->isDate()) && type->isTimestamp()) {
112-
const auto operand_unit = (operand_type->isTimestamp())
113-
? operand_type->as<hdk::ir::TimestampType>()->unit()
114-
: hdk::ir::TimeUnit::kSecond;
115-
if (operand_unit != type->as<hdk::ir::TimestampType>()->unit()) {
116-
return codegenCastBetweenTimestamps(
117-
operand_lv, operand_type, type, type->nullable());
118-
}
92+
if (type->isBoolean()) {
93+
return operand_lv;
11994
}
120-
if (type->isInteger() || type->isDecimal() || type->isDateTime()) {
121-
return codegenCastBetweenIntTypes(operand_lv, operand_type, type);
122-
} else {
123-
return codegenCastToFp(operand_lv, operand_type, type);
95+
}
96+
if (operand_type->isInteger() && operand_lv->getType()->isIntegerTy(8) &&
97+
type->isBoolean()) {
98+
// cast int8 to boolean
99+
return codegenCastBetweenIntTypes(operand_lv, operand_type, type);
100+
}
101+
if (operand_type->isTimestamp() && type->isDate()) {
102+
// Maybe we should instead generate DateTruncExpr directly from RelAlgTranslator
103+
// for this pattern. However, DateTruncExpr is supposed to return a timestamp,
104+
// whereas this cast returns a date. The underlying type for both is still the same,
105+
// but it still doesn't look like a good idea to misuse DateTruncExpr.
106+
// Date will have default precision of day, but TIMESTAMP dimension would
107+
// matter but while converting date through seconds
108+
return codegenCastTimestampToDate(
109+
operand_lv, operand_type->as<hdk::ir::TimestampType>()->unit(), type->nullable());
110+
}
111+
if (operand_type->isTimestamp() && type->isTime()) {
112+
return codegenCastTimestampToTime(operand_lv, operand_type, type);
113+
}
114+
if ((operand_type->isTimestamp() || operand_type->isDate()) && type->isTimestamp()) {
115+
const auto operand_unit = (operand_type->isTimestamp())
116+
? operand_type->as<hdk::ir::TimestampType>()->unit()
117+
: hdk::ir::TimeUnit::kSecond;
118+
if (operand_unit != type->as<hdk::ir::TimestampType>()->unit()) {
119+
return codegenCastBetweenTimestamps(operand_lv, operand_type, type);
124120
}
121+
}
122+
if (type->isInteger() || type->isDecimal() || type->isDateTime()) {
123+
return codegenCastBetweenIntTypes(operand_lv, operand_type, type);
125124
} else {
126-
return codegenCastFromFp(operand_lv, operand_type, type);
125+
return codegenCastToFp(operand_lv, operand_type, type);
127126
}
127+
128128
CHECK(false);
129129
return nullptr;
130130
}
@@ -159,11 +159,53 @@ llvm::Value* CodeGenerator::codegenCastTimestampToDate(llvm::Value* ts_lv,
159159
return ret;
160160
}
161161

162+
llvm::Value* CodeGenerator::codegenCastTimestampToTime(llvm::Value* ts_lv,
163+
const hdk::ir::Type* operand_type,
164+
const hdk::ir::Type* target_type) {
165+
AUTOMATIC_IR_METADATA(cgen_state_);
166+
const auto operand_unit = operand_type->as<hdk::ir::TimestampType>()->unit();
167+
const auto target_unit = target_type->as<hdk::ir::TimeType>()->unit();
168+
169+
CHECK(ts_lv->getType()->isIntegerTy(64));
170+
if (operand_unit < target_unit) {
171+
const auto target_sec_scale = hdk::ir::unitsPerSecond(target_unit);
172+
const auto scale =
173+
hdk::ir::unitsPerSecond(target_unit) / hdk::ir::unitsPerSecond(operand_unit);
174+
if (!operand_type->nullable()) {
175+
return cgen_state_->emitCall(
176+
"TimestampToTimeUpscale",
177+
{{ts_lv, cgen_state_->llInt(scale), cgen_state_->llInt(target_sec_scale)}});
178+
}
179+
// Timestamp, Time32. Time64 have int64 inline int value, but logically corect to pass
180+
// target and source null values.
181+
return cgen_state_->emitCall("TimestampToTimeUpscaleNullable",
182+
{{ts_lv,
183+
cgen_state_->llInt(scale),
184+
cgen_state_->llInt(target_sec_scale),
185+
cgen_state_->inlineIntNull(operand_type),
186+
cgen_state_->inlineIntNull(target_type)}});
187+
}
188+
// On downscale we at first cut of date part, than cut of unused unit precision
189+
const auto operand_sec_scale = hdk::ir::unitsPerSecond(operand_unit);
190+
const auto scale =
191+
hdk::ir::unitsPerSecond(operand_unit) / hdk::ir::unitsPerSecond(target_unit);
192+
if (!operand_type->nullable()) {
193+
return cgen_state_->emitCall(
194+
"TimestampToTimeDownscale",
195+
{{ts_lv, cgen_state_->llInt(scale), cgen_state_->llInt(operand_sec_scale)}});
196+
}
197+
return cgen_state_->emitCall("TimestampToTimeDownscaleNullable",
198+
{{ts_lv,
199+
cgen_state_->llInt(scale),
200+
cgen_state_->llInt(operand_sec_scale),
201+
cgen_state_->inlineIntNull(operand_type),
202+
cgen_state_->inlineIntNull(target_type)}});
203+
}
204+
162205
llvm::Value* CodeGenerator::codegenCastBetweenTimestamps(
163206
llvm::Value* ts_lv,
164207
const hdk::ir::Type* operand_type,
165-
const hdk::ir::Type* target_type,
166-
const bool nullable) {
208+
const hdk::ir::Type* target_type) {
167209
AUTOMATIC_IR_METADATA(cgen_state_);
168210
const auto operand_unit = operand_type->isTimestamp()
169211
? operand_type->as<hdk::ir::TimestampType>()->unit()
@@ -179,7 +221,7 @@ llvm::Value* CodeGenerator::codegenCastBetweenTimestamps(
179221
const auto scale =
180222
hdk::ir::unitsPerSecond(target_unit) / hdk::ir::unitsPerSecond(operand_unit);
181223
codegenCastBetweenIntTypesOverflowChecks(ts_lv, operand_type, target_type, scale);
182-
return nullable
224+
return target_type->nullable()
183225
? cgen_state_->emitCall("mul_int64_t_nullable_lhs",
184226
{ts_lv,
185227
cgen_state_->llInt(static_cast<int64_t>(scale)),
@@ -189,7 +231,7 @@ llvm::Value* CodeGenerator::codegenCastBetweenTimestamps(
189231
}
190232
const auto scale =
191233
hdk::ir::unitsPerSecond(operand_unit) / hdk::ir::unitsPerSecond(target_unit);
192-
return nullable
234+
return target_type->nullable()
193235
? cgen_state_->emitCall("floor_div_nullable_lhs",
194236
{ts_lv,
195237
cgen_state_->llInt(static_cast<int64_t>(scale)),

omniscidb/QueryEngine/CodeGenerator.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,13 @@ class CodeGenerator {
265265
const hdk::ir::TimeUnit unit,
266266
const bool nullable);
267267

268+
llvm::Value* codegenCastTimestampToTime(llvm::Value* ts_lv,
269+
const hdk::ir::Type* operand_type,
270+
const hdk::ir::Type* target_type);
271+
268272
llvm::Value* codegenCastBetweenTimestamps(llvm::Value* ts_lv,
269273
const hdk::ir::Type* operand_type,
270-
const hdk::ir::Type* target_type,
271-
const bool nullable);
274+
const hdk::ir::Type* target_type);
272275

273276
llvm::Value* codegenCastFromString(llvm::Value* operand_lv,
274277
const hdk::ir::Type* operand_type,

omniscidb/QueryEngine/DateTruncate.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,66 @@ DateTruncateHighPrecisionToDateNullable(const int64_t timeval,
330330
return truncate_high_precision_timestamp_to_date(timeval, scale);
331331
}
332332

333+
namespace {
334+
// scale is 10^{3,6,9}
335+
inline DEVICE int64_t
336+
timestamp_to_time_truncation_upscale(const int64_t timeval,
337+
const int64_t conversion_scale,
338+
const int64_t target_sec_scale) {
339+
return unsigned_mod(timeval * conversion_scale, target_sec_scale * kSecsPerDay);
340+
}
341+
342+
// scale is 10^{3,6,9}
343+
inline DEVICE int64_t
344+
timestamp_to_time_truncation_downscale(const int64_t timeval,
345+
const int64_t conversion_scale,
346+
const int64_t target_sec_scale) {
347+
return unsigned_mod(timeval, target_sec_scale * kSecsPerDay) / conversion_scale;
348+
}
349+
} // namespace
350+
351+
extern "C" DEVICE RUNTIME_EXPORT ALWAYS_INLINE int64_t
352+
TimestampToTimeUpscale(const int64_t timeval,
353+
const int64_t conversion_scale,
354+
const int64_t target_sec_scale) {
355+
return timestamp_to_time_truncation_upscale(
356+
timeval, conversion_scale, target_sec_scale);
357+
}
358+
359+
extern "C" DEVICE RUNTIME_EXPORT ALWAYS_INLINE int64_t
360+
TimestampToTimeDownscale(const int64_t timeval,
361+
const int64_t conversion_scale,
362+
const int64_t target_sec_scale) {
363+
return timestamp_to_time_truncation_downscale(
364+
timeval, conversion_scale, target_sec_scale);
365+
}
366+
367+
extern "C" RUNTIME_EXPORT ALWAYS_INLINE DEVICE int64_t
368+
TimestampToTimeUpscaleNullable(const int64_t timeval,
369+
const int64_t conversion_scale,
370+
const int64_t target_sec_scale,
371+
const int64_t source_null_val,
372+
const int64_t target_null_val) {
373+
if (timeval == source_null_val) {
374+
return target_null_val;
375+
}
376+
return timestamp_to_time_truncation_upscale(
377+
timeval, conversion_scale, target_sec_scale);
378+
}
379+
380+
extern "C" RUNTIME_EXPORT ALWAYS_INLINE DEVICE int64_t
381+
TimestampToTimeDownscaleNullable(const int64_t timeval,
382+
const int64_t conversion_scale,
383+
const int64_t target_sec_scale,
384+
const int64_t source_null_val,
385+
const int64_t target_null_val) {
386+
if (timeval == source_null_val) {
387+
return target_null_val;
388+
}
389+
return timestamp_to_time_truncation_downscale(
390+
timeval, conversion_scale, target_sec_scale);
391+
}
392+
333393
namespace {
334394

335395
struct EraTime {

0 commit comments

Comments
 (0)