duckdb 0.8.2-dev3092.0 → 0.8.2-dev3190.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
4
  "types": "./lib/duckdb.d.ts",
5
- "version": "0.8.2-dev3092.0",
5
+ "version": "0.8.2-dev3190.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -14,7 +14,8 @@
14
14
  namespace duckdb {
15
15
 
16
16
  struct ICUDatePart : public ICUDateFunc {
17
- typedef int64_t (*part_adapter_t)(icu::Calendar *calendar, const uint64_t micros);
17
+ typedef int64_t (*part_bigint_t)(icu::Calendar *calendar, const uint64_t micros);
18
+ typedef double (*part_double_t)(icu::Calendar *calendar, const uint64_t micros);
18
19
 
19
20
  // Date part adapters
20
21
  static int64_t ExtractEra(icu::Calendar *calendar, const uint64_t micros) {
@@ -129,7 +130,26 @@ struct ICUDatePart : public ICUDateFunc {
129
130
  return (secs % Interval::SECS_PER_HOUR) / Interval::SECS_PER_MINUTE;
130
131
  }
131
132
 
132
- static part_adapter_t PartCodeAdapterFactory(DatePartSpecifier part) {
133
+ // PG uses doubles for JDs so we can only use them with other double types
134
+ static double ExtractJulianDay(icu::Calendar *calendar, const uint64_t micros) {
135
+ // We need days + fraction
136
+ auto days = ExtractField(calendar, UCAL_JULIAN_DAY);
137
+ auto frac = ExtractHour(calendar, micros);
138
+
139
+ frac *= Interval::MINS_PER_HOUR;
140
+ frac += ExtractMinute(calendar, micros);
141
+
142
+ frac *= Interval::MICROS_PER_MINUTE;
143
+ frac += ExtractMicrosecond(calendar, micros);
144
+
145
+ double result = frac;
146
+ result /= Interval::MICROS_PER_DAY;
147
+ result += days;
148
+
149
+ return result;
150
+ }
151
+
152
+ static part_bigint_t PartCodeBigintFactory(DatePartSpecifier part) {
133
153
  switch (part) {
134
154
  case DatePartSpecifier::YEAR:
135
155
  return ExtractYear;
@@ -178,7 +198,16 @@ struct ICUDatePart : public ICUDateFunc {
178
198
  case DatePartSpecifier::TIMEZONE_MINUTE:
179
199
  return ExtractTimezoneMinute;
180
200
  default:
181
- throw Exception("Unsupported ICU extract adapter");
201
+ throw Exception("Unsupported ICU BIGINT extractor");
202
+ }
203
+ }
204
+
205
+ static part_double_t PartCodeDoubleFactory(DatePartSpecifier part) {
206
+ switch (part) {
207
+ case DatePartSpecifier::JULIAN_DAY:
208
+ return ExtractJulianDay;
209
+ default:
210
+ throw Exception("Unsupported ICU DOUBLE extractor");
182
211
  }
183
212
  }
184
213
 
@@ -208,7 +237,7 @@ struct ICUDatePart : public ICUDateFunc {
208
237
  static string_t MonthName(icu::Calendar *calendar, const uint64_t micros) {
209
238
  const auto mm = ExtractMonth(calendar, micros) - 1;
210
239
  if (mm == 12) {
211
- return "Undecember";
240
+ return "Undecimber";
212
241
  }
213
242
  return Date::MONTH_NAMES[mm];
214
243
  }
@@ -282,7 +311,7 @@ struct ICUDatePart : public ICUDateFunc {
282
311
  [&](string_t specifier, INPUT_TYPE input, ValidityMask &mask, idx_t idx) {
283
312
  if (Timestamp::IsFinite(input)) {
284
313
  const auto micros = SetTime(calendar, input);
285
- auto adapter = PartCodeAdapterFactory(GetDatePartSpecifier(specifier.GetString()));
314
+ auto adapter = PartCodeBigintFactory(GetDatePartSpecifier(specifier.GetString()));
286
315
  return adapter(calendar, micros);
287
316
  } else {
288
317
  mask.SetInvalid(idx);
@@ -291,11 +320,37 @@ struct ICUDatePart : public ICUDateFunc {
291
320
  });
292
321
  }
293
322
 
323
+ struct BindStructData : public BindData {
324
+ using part_codes_t = vector<DatePartSpecifier>;
325
+ using bigints_t = vector<part_bigint_t>;
326
+ using doubles_t = vector<part_double_t>;
327
+
328
+ BindStructData(ClientContext &context, part_codes_t &part_codes_p, bigints_t &bigints_p, doubles_t &doubles_p)
329
+ : BindData(context), part_codes(part_codes_p), bigints(bigints_p), doubles(doubles_p) {
330
+ }
331
+ BindStructData(const BindStructData &other)
332
+ : BindData(other), part_codes(other.part_codes), bigints(other.bigints), doubles(other.doubles) {
333
+ }
334
+
335
+ part_codes_t part_codes;
336
+ bigints_t bigints;
337
+ doubles_t doubles;
338
+
339
+ bool Equals(const FunctionData &other_p) const override {
340
+ const auto &other = other_p.Cast<BindStructData>();
341
+ return BindData::Equals(other_p) && part_codes == other.part_codes && bigints == other.bigints &&
342
+ doubles == other.doubles;
343
+ }
344
+
345
+ duckdb::unique_ptr<FunctionData> Copy() const override {
346
+ return make_uniq<BindStructData>(*this);
347
+ }
348
+ };
349
+
294
350
  template <typename INPUT_TYPE>
295
351
  static void StructFunction(DataChunk &args, ExpressionState &state, Vector &result) {
296
- using BIND_TYPE = BindAdapterData<int64_t>;
297
352
  auto &func_expr = state.expr.Cast<BoundFunctionExpression>();
298
- auto &info = func_expr.bind_info->Cast<BIND_TYPE>();
353
+ auto &info = func_expr.bind_info->Cast<BindStructData>();
299
354
  CalendarPtr calendar_ptr(info.calendar->clone());
300
355
  auto calendar = calendar_ptr.get();
301
356
 
@@ -318,9 +373,15 @@ struct ICUDatePart : public ICUDateFunc {
318
373
  auto &child_entry = child_entries[col];
319
374
  if (is_finite) {
320
375
  ConstantVector::SetNull(*child_entry, false);
321
- auto pdata = ConstantVector::GetData<int64_t>(*child_entry);
322
- auto adapter = info.adapters[col];
323
- pdata[0] = adapter(calendar, micros);
376
+ if (IsBigintDatepart(info.part_codes[col])) {
377
+ auto pdata = ConstantVector::GetData<int64_t>(*child_entry);
378
+ auto adapter = info.bigints[col];
379
+ pdata[0] = adapter(calendar, micros);
380
+ } else {
381
+ auto pdata = ConstantVector::GetData<double>(*child_entry);
382
+ auto adapter = info.doubles[col];
383
+ pdata[0] = adapter(calendar, micros);
384
+ }
324
385
  } else {
325
386
  ConstantVector::SetNull(*child_entry, true);
326
387
  }
@@ -350,9 +411,15 @@ struct ICUDatePart : public ICUDateFunc {
350
411
  auto &child_entry = child_entries[col];
351
412
  if (is_finite) {
352
413
  FlatVector::Validity(*child_entry).SetValid(i);
353
- auto pdata = FlatVector::GetData<int64_t>(*child_entry);
354
- auto adapter = info.adapters[col];
355
- pdata[i] = adapter(calendar, micros);
414
+ if (IsBigintDatepart(info.part_codes[col])) {
415
+ auto pdata = ConstantVector::GetData<int64_t>(*child_entry);
416
+ auto adapter = info.bigints[col];
417
+ pdata[i] = adapter(calendar, micros);
418
+ } else {
419
+ auto pdata = ConstantVector::GetData<double>(*child_entry);
420
+ auto adapter = info.doubles[col];
421
+ pdata[i] = adapter(calendar, micros);
422
+ }
356
423
  } else {
357
424
  FlatVector::Validity(*child_entry).SetInvalid(i);
358
425
  }
@@ -376,19 +443,54 @@ struct ICUDatePart : public ICUDateFunc {
376
443
  return make_uniq<BIND_TYPE>(context, adapter);
377
444
  }
378
445
 
379
- static duckdb::unique_ptr<FunctionData> BindDatePart(ClientContext &context, ScalarFunction &bound_function,
380
- vector<duckdb::unique_ptr<Expression>> &arguments) {
446
+ static duckdb::unique_ptr<FunctionData> BindUnaryDatePart(ClientContext &context, ScalarFunction &bound_function,
447
+ vector<duckdb::unique_ptr<Expression>> &arguments) {
448
+ const auto part_code = GetDatePartSpecifier(bound_function.name);
449
+ if (IsBigintDatepart(part_code)) {
450
+ using data_t = BindAdapterData<int64_t>;
451
+ auto adapter = PartCodeBigintFactory(part_code);
452
+ return BindAdapter<data_t>(context, bound_function, arguments, adapter);
453
+ } else {
454
+ using data_t = BindAdapterData<double>;
455
+ auto adapter = PartCodeDoubleFactory(part_code);
456
+ return BindAdapter<data_t>(context, bound_function, arguments, adapter);
457
+ }
458
+ }
459
+
460
+ static duckdb::unique_ptr<FunctionData> BindBinaryDatePart(ClientContext &context, ScalarFunction &bound_function,
461
+ vector<duckdb::unique_ptr<Expression>> &arguments) {
462
+ // If we are only looking for Julian Days, then patch in the unary function.
463
+ do {
464
+ if (arguments[0]->HasParameter() || !arguments[0]->IsFoldable()) {
465
+ break;
466
+ }
467
+
468
+ Value part_value = ExpressionExecutor::EvaluateScalar(context, *arguments[0]);
469
+ if (part_value.IsNull()) {
470
+ break;
471
+ }
472
+
473
+ const auto part_name = part_value.ToString();
474
+ const auto part_code = GetDatePartSpecifier(part_name);
475
+ if (IsBigintDatepart(part_code)) {
476
+ break;
477
+ }
478
+
479
+ arguments.erase(arguments.begin());
480
+ bound_function.arguments.erase(bound_function.arguments.begin());
481
+ bound_function.name = part_name;
482
+ bound_function.return_type = LogicalType::DOUBLE;
483
+ bound_function.function = UnaryTimestampFunction<timestamp_t, double>;
484
+
485
+ return BindUnaryDatePart(context, bound_function, arguments);
486
+ } while (false);
487
+
381
488
  using data_t = BindAdapterData<int64_t>;
382
- auto adapter =
383
- (arguments.size() == 1) ? PartCodeAdapterFactory(GetDatePartSpecifier(bound_function.name)) : nullptr;
384
- return BindAdapter<data_t>(context, bound_function, arguments, adapter);
489
+ return BindAdapter<data_t>(context, bound_function, arguments, nullptr);
385
490
  }
386
491
 
387
492
  static duckdb::unique_ptr<FunctionData> BindStruct(ClientContext &context, ScalarFunction &bound_function,
388
493
  vector<duckdb::unique_ptr<Expression>> &arguments) {
389
- using data_t = BindAdapterData<int64_t>;
390
- using adapters_t = data_t::adapters_t;
391
-
392
494
  // collect names and deconflict, construct return type
393
495
  if (arguments[0]->HasParameter()) {
394
496
  throw ParameterNotResolvedException();
@@ -399,7 +501,9 @@ struct ICUDatePart : public ICUDateFunc {
399
501
 
400
502
  case_insensitive_set_t name_collision_set;
401
503
  child_list_t<LogicalType> struct_children;
402
- adapters_t adapters;
504
+ BindStructData::part_codes_t part_codes;
505
+ BindStructData::bigints_t bigints;
506
+ BindStructData::doubles_t doubles;
403
507
 
404
508
  Value parts_list = ExpressionExecutor::EvaluateScalar(context, *arguments[0]);
405
509
  if (parts_list.type().id() == LogicalTypeId::LIST) {
@@ -407,7 +511,11 @@ struct ICUDatePart : public ICUDateFunc {
407
511
  if (list_children.empty()) {
408
512
  throw BinderException("%s requires non-empty lists of part names", bound_function.name);
409
513
  }
410
- for (const auto &part_value : list_children) {
514
+
515
+ bigints.resize(list_children.size(), nullptr);
516
+ doubles.resize(list_children.size(), nullptr);
517
+ for (size_t col = 0; col < list_children.size(); ++col) {
518
+ const auto &part_value = list_children[col];
411
519
  if (part_value.IsNull()) {
412
520
  throw BinderException("NULL struct entry name in %s", bound_function.name);
413
521
  }
@@ -417,8 +525,14 @@ struct ICUDatePart : public ICUDateFunc {
417
525
  throw BinderException("Duplicate struct entry name \"%s\" in %s", part_name, bound_function.name);
418
526
  }
419
527
  name_collision_set.insert(part_name);
420
- adapters.emplace_back(PartCodeAdapterFactory(part_code));
421
- struct_children.emplace_back(make_pair(part_name, LogicalType::BIGINT));
528
+ part_codes.emplace_back(part_code);
529
+ if (IsBigintDatepart(part_code)) {
530
+ bigints[col] = PartCodeBigintFactory(part_code);
531
+ struct_children.emplace_back(make_pair(part_name, LogicalType::BIGINT));
532
+ } else {
533
+ doubles[col] = PartCodeDoubleFactory(part_code);
534
+ struct_children.emplace_back(make_pair(part_name, LogicalType::DOUBLE));
535
+ }
422
536
  }
423
537
  } else {
424
538
  throw BinderException("%s can only take constant lists of part names", bound_function.name);
@@ -426,7 +540,7 @@ struct ICUDatePart : public ICUDateFunc {
426
540
 
427
541
  Function::EraseArgument(bound_function, arguments, 0);
428
542
  bound_function.return_type = LogicalType::STRUCT(std::move(struct_children));
429
- return make_uniq<data_t>(context, adapters);
543
+ return make_uniq<BindStructData>(context, part_codes, bigints, doubles);
430
544
  }
431
545
 
432
546
  static void SerializeFunction(FieldWriter &writer, const FunctionData *bind_data_p,
@@ -440,15 +554,18 @@ struct ICUDatePart : public ICUDateFunc {
440
554
  }
441
555
 
442
556
  template <typename INPUT_TYPE, typename RESULT_TYPE>
443
- static ScalarFunction GetUnaryPartCodeFunction(const LogicalType &temporal_type) {
444
- return ScalarFunction({temporal_type}, LogicalType::BIGINT, UnaryTimestampFunction<INPUT_TYPE, RESULT_TYPE>,
445
- BindDatePart);
557
+ static ScalarFunction GetUnaryPartCodeFunction(const LogicalType &temporal_type,
558
+ const LogicalType &result_type = LogicalType::BIGINT) {
559
+ return ScalarFunction({temporal_type}, result_type, UnaryTimestampFunction<INPUT_TYPE, RESULT_TYPE>,
560
+ BindUnaryDatePart);
446
561
  }
447
562
 
448
- static void AddUnaryPartCodeFunctions(const string &name, ClientContext &context) {
563
+ template <typename RESULT_TYPE = int64_t>
564
+ static void AddUnaryPartCodeFunctions(const string &name, ClientContext &context,
565
+ const LogicalType &result_type = LogicalType::BIGINT) {
449
566
  auto &catalog = Catalog::GetSystemCatalog(context);
450
567
  ScalarFunctionSet set(name);
451
- set.AddFunction(GetUnaryPartCodeFunction<timestamp_t, int64_t>(LogicalType::TIMESTAMP_TZ));
568
+ set.AddFunction(GetUnaryPartCodeFunction<timestamp_t, RESULT_TYPE>(LogicalType::TIMESTAMP_TZ, result_type));
452
569
  CreateScalarFunctionInfo func_info(set);
453
570
  catalog.AddFunction(context, func_info);
454
571
  }
@@ -456,7 +573,7 @@ struct ICUDatePart : public ICUDateFunc {
456
573
  template <typename INPUT_TYPE, typename RESULT_TYPE>
457
574
  static ScalarFunction GetBinaryPartCodeFunction(const LogicalType &temporal_type) {
458
575
  return ScalarFunction({LogicalType::VARCHAR, temporal_type}, LogicalType::BIGINT,
459
- BinaryTimestampFunction<INPUT_TYPE, RESULT_TYPE>, BindDatePart);
576
+ BinaryTimestampFunction<INPUT_TYPE, RESULT_TYPE>, BindBinaryDatePart);
460
577
  }
461
578
 
462
579
  template <typename INPUT_TYPE>
@@ -561,6 +678,8 @@ void RegisterICUDatePartFunctions(ClientContext &context) {
561
678
  ICUDatePart::AddUnaryPartCodeFunctions("timezone_hour", context);
562
679
  ICUDatePart::AddUnaryPartCodeFunctions("timezone_minute", context);
563
680
 
681
+ ICUDatePart::AddUnaryPartCodeFunctions<double>("julian", context, LogicalType::DOUBLE);
682
+
564
683
  // register combinations
565
684
  ICUDatePart::AddUnaryPartCodeFunctions("yearweek", context); // Note this is ISO year and week
566
685
 
@@ -171,6 +171,7 @@ ICUDateFunc::part_sub_t ICUDateFunc::SubtractFactory(DatePartSpecifier type) {
171
171
  case DatePartSpecifier::DOW:
172
172
  case DatePartSpecifier::ISODOW:
173
173
  case DatePartSpecifier::DOY:
174
+ case DatePartSpecifier::JULIAN_DAY:
174
175
  return ICUCalendarSub::SubtractDay;
175
176
  case DatePartSpecifier::HOUR:
176
177
  return ICUCalendarSub::SubtractHour;
@@ -175,6 +175,7 @@ ICUDateFunc::part_trunc_t ICUDateFunc::TruncationFactory(DatePartSpecifier type)
175
175
  case DatePartSpecifier::DOW:
176
176
  case DatePartSpecifier::ISODOW:
177
177
  case DatePartSpecifier::DOY:
178
+ case DatePartSpecifier::JULIAN_DAY:
178
179
  return ICUDateTrunc::TruncDay;
179
180
  case DatePartSpecifier::HOUR:
180
181
  return ICUDateTrunc::TruncHour;
@@ -1154,6 +1154,8 @@ const char* EnumUtil::ToChars<DatePartSpecifier>(DatePartSpecifier value) {
1154
1154
  return "TIMEZONE_HOUR";
1155
1155
  case DatePartSpecifier::TIMEZONE_MINUTE:
1156
1156
  return "TIMEZONE_MINUTE";
1157
+ case DatePartSpecifier::JULIAN_DAY:
1158
+ return "JULIAN_DAY";
1157
1159
  default:
1158
1160
  throw NotImplementedException(StringUtil::Format("Enum value: '%d' not implemented", value));
1159
1161
  }
@@ -1230,6 +1232,9 @@ DatePartSpecifier EnumUtil::FromString<DatePartSpecifier>(const char *value) {
1230
1232
  if (StringUtil::Equals(value, "TIMEZONE_MINUTE")) {
1231
1233
  return DatePartSpecifier::TIMEZONE_MINUTE;
1232
1234
  }
1235
+ if (StringUtil::Equals(value, "JULIAN_DAY")) {
1236
+ return DatePartSpecifier::JULIAN_DAY;
1237
+ }
1233
1238
  throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value));
1234
1239
  }
1235
1240
 
@@ -65,6 +65,8 @@ bool TryGetDatePartSpecifier(const string &specifier_p, DatePartSpecifier &resul
65
65
  result = DatePartSpecifier::TIMEZONE_HOUR;
66
66
  } else if (specifier == "timezone_minute") {
67
67
  result = DatePartSpecifier::TIMEZONE_MINUTE;
68
+ } else if (specifier == "julian" || specifier == "jd") {
69
+ result = DatePartSpecifier::JULIAN_DAY;
68
70
  } else {
69
71
  return false;
70
72
  }
@@ -490,6 +490,12 @@ int32_t Date::ExtractDayOfTheYear(date_t date) {
490
490
  return date.days - Date::CUMULATIVE_YEAR_DAYS[year_offset] + 1;
491
491
  }
492
492
 
493
+ int64_t Date::ExtractJulianDay(date_t date) {
494
+ // Julian Day 0 is (-4713, 11, 24) in the proleptic Gregorian calendar.
495
+ static const auto JULIAN_EPOCH = -2440588;
496
+ return date.days - JULIAN_EPOCH;
497
+ }
498
+
493
499
  int32_t Date::ExtractISODayOfTheWeek(date_t date) {
494
500
  // date of 0 is 1970-01-01, which was a Thursday (4)
495
501
  // -7 = 4
@@ -341,4 +341,11 @@ int64_t Timestamp::GetEpochNanoSeconds(timestamp_t timestamp) {
341
341
  return result;
342
342
  }
343
343
 
344
+ double Timestamp::GetJulianDay(timestamp_t timestamp) {
345
+ double result = Timestamp::GetTime(timestamp).micros;
346
+ result /= Interval::MICROS_PER_DAY;
347
+ result += Date::ExtractJulianDay(Timestamp::GetDate(timestamp));
348
+ return result;
349
+ }
350
+
344
351
  } // namespace duckdb
@@ -182,6 +182,7 @@ static StaticFunctionDefinition internal_functions[] = {
182
182
  DUCKDB_SCALAR_FUNCTION(JaccardFun),
183
183
  DUCKDB_SCALAR_FUNCTION(JaroSimilarityFun),
184
184
  DUCKDB_SCALAR_FUNCTION(JaroWinklerSimilarityFun),
185
+ DUCKDB_SCALAR_FUNCTION_SET(JulianDayFun),
185
186
  DUCKDB_AGGREGATE_FUNCTION(KahanSumFun),
186
187
  DUCKDB_AGGREGATE_FUNCTION(KurtosisFun),
187
188
  DUCKDB_SCALAR_FUNCTION_SET(LastDayFun),
@@ -304,6 +304,7 @@ static int64_t DifferenceDates(DatePartSpecifier type, TA startdate, TB enddate)
304
304
  case DatePartSpecifier::DOW:
305
305
  case DatePartSpecifier::ISODOW:
306
306
  case DatePartSpecifier::DOY:
307
+ case DatePartSpecifier::JULIAN_DAY:
307
308
  return DateDiff::DayOperator::template Operation<TA, TB, TR>(startdate, enddate);
308
309
  case DatePartSpecifier::DECADE:
309
310
  return DateDiff::DecadeOperator::template Operation<TA, TB, TR>(startdate, enddate);
@@ -359,6 +360,7 @@ static void DateDiffBinaryExecutor(DatePartSpecifier type, Vector &left, Vector
359
360
  case DatePartSpecifier::DOW:
360
361
  case DatePartSpecifier::ISODOW:
361
362
  case DatePartSpecifier::DOY:
363
+ case DatePartSpecifier::JULIAN_DAY:
362
364
  DateDiff::BinaryExecute<TA, TB, TR, DateDiff::DayOperator>(left, right, result, count);
363
365
  break;
364
366
  case DatePartSpecifier::DECADE:
@@ -37,6 +37,7 @@ DatePartSpecifier GetDateTypePartSpecifier(const string &specifier, LogicalType
37
37
  case DatePartSpecifier::DOY:
38
38
  case DatePartSpecifier::YEARWEEK:
39
39
  case DatePartSpecifier::ERA:
40
+ case DatePartSpecifier::JULIAN_DAY:
40
41
  return part;
41
42
  default:
42
43
  break;
@@ -97,8 +98,9 @@ static unique_ptr<BaseStatistics> PropagateSimpleDatePartStatistics(vector<BaseS
97
98
  }
98
99
 
99
100
  struct DatePart {
100
- template <class T, class OP>
101
- static unique_ptr<BaseStatistics> PropagateDatePartStatistics(vector<BaseStatistics> &child_stats) {
101
+ template <class T, class OP, class TR = int64_t>
102
+ static unique_ptr<BaseStatistics> PropagateDatePartStatistics(vector<BaseStatistics> &child_stats,
103
+ const LogicalType &stats_type = LogicalType::BIGINT) {
102
104
  // we can only propagate complex date part stats if the child has stats
103
105
  auto &nstats = child_stats[0];
104
106
  if (!NumericStats::HasMinMax(nstats)) {
@@ -114,11 +116,11 @@ struct DatePart {
114
116
  if (!Value::IsFinite(min) || !Value::IsFinite(max)) {
115
117
  return nullptr;
116
118
  }
117
- auto min_part = OP::template Operation<T, int64_t>(min);
118
- auto max_part = OP::template Operation<T, int64_t>(max);
119
- auto result = NumericStats::CreateEmpty(LogicalType::BIGINT);
120
- NumericStats::SetMin(result, Value::BIGINT(min_part));
121
- NumericStats::SetMax(result, Value::BIGINT(max_part));
119
+ TR min_part = OP::template Operation<T, TR>(min);
120
+ TR max_part = OP::template Operation<T, TR>(max);
121
+ auto result = NumericStats::CreateEmpty(stats_type);
122
+ NumericStats::SetMin(result, Value(min_part));
123
+ NumericStats::SetMax(result, Value(max_part));
122
124
  result.CopyValidity(child_stats[0]);
123
125
  return result.ToUnique();
124
126
  }
@@ -499,6 +501,18 @@ struct DatePart {
499
501
  }
500
502
  };
501
503
 
504
+ struct JulianDayOperator {
505
+ template <class TA, class TR>
506
+ static inline TR Operation(TA input) {
507
+ return Timestamp::GetJulianDay(input);
508
+ }
509
+
510
+ template <class T>
511
+ static unique_ptr<BaseStatistics> PropagateStatistics(ClientContext &context, FunctionStatisticsInput &input) {
512
+ return PropagateDatePartStatistics<T, JulianDayOperator, double>(input.child_stats, LogicalType::DOUBLE);
513
+ }
514
+ };
515
+
502
516
  // These are all zero and have the same restrictions
503
517
  using TimezoneHourOperator = TimezoneOperator;
504
518
  using TimezoneMinuteOperator = TimezoneOperator;
@@ -514,7 +528,8 @@ struct DatePart {
514
528
  EPOCH = 1 << 3,
515
529
  TIME = 1 << 4,
516
530
  ZONE = 1 << 5,
517
- ISO = 1 << 6
531
+ ISO = 1 << 6,
532
+ JD = 1 << 7
518
533
  };
519
534
 
520
535
  static part_mask_t GetMask(const part_codes_t &part_codes) {
@@ -546,6 +561,9 @@ struct DatePart {
546
561
  case DatePartSpecifier::EPOCH:
547
562
  mask |= EPOCH;
548
563
  break;
564
+ case DatePartSpecifier::JULIAN_DAY:
565
+ mask |= JD;
566
+ break;
549
567
  case DatePartSpecifier::MICROSECONDS:
550
568
  case DatePartSpecifier::MILLISECONDS:
551
569
  case DatePartSpecifier::SECOND:
@@ -564,63 +582,73 @@ struct DatePart {
564
582
  }
565
583
 
566
584
  template <typename P>
567
- static inline P HasPartValue(P *part_values, DatePartSpecifier part) {
568
- return part_values[int(part)];
585
+ static inline P HasPartValue(vector<P> part_values, DatePartSpecifier part) {
586
+ static const auto BEGIN_DOUBLE = size_t(DatePartSpecifier::JULIAN_DAY);
587
+ auto idx = size_t(part);
588
+ if (IsBigintDatepart(part)) {
589
+ return part_values[idx];
590
+ } else {
591
+ return part_values[idx - BEGIN_DOUBLE];
592
+ }
569
593
  }
570
594
 
571
- template <class TA, class TR>
572
- static inline void Operation(TR **part_values, const TA &input, const idx_t idx, const part_mask_t mask) {
573
- TR *part_data;
595
+ using bigint_vec = vector<int64_t *>;
596
+ using double_vec = vector<double *>;
597
+
598
+ template <class TA>
599
+ static inline void Operation(bigint_vec &bigint_values, double_vec &double_values, const TA &input,
600
+ const idx_t idx, const part_mask_t mask) {
601
+ int64_t *bigint_data;
574
602
  // YMD calculations
575
603
  int32_t yyyy = 1970;
576
604
  int32_t mm = 0;
577
605
  int32_t dd = 1;
578
606
  if (mask & YMD) {
579
607
  Date::Convert(input, yyyy, mm, dd);
580
- part_data = HasPartValue(part_values, DatePartSpecifier::YEAR);
581
- if (part_data) {
582
- part_data[idx] = yyyy;
608
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::YEAR);
609
+ if (bigint_data) {
610
+ bigint_data[idx] = yyyy;
583
611
  }
584
- part_data = HasPartValue(part_values, DatePartSpecifier::MONTH);
585
- if (part_data) {
586
- part_data[idx] = mm;
612
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::MONTH);
613
+ if (bigint_data) {
614
+ bigint_data[idx] = mm;
587
615
  }
588
- part_data = HasPartValue(part_values, DatePartSpecifier::DAY);
589
- if (part_data) {
590
- part_data[idx] = dd;
616
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::DAY);
617
+ if (bigint_data) {
618
+ bigint_data[idx] = dd;
591
619
  }
592
- part_data = HasPartValue(part_values, DatePartSpecifier::DECADE);
593
- if (part_data) {
594
- part_data[idx] = DecadeOperator::DecadeFromYear(yyyy);
620
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::DECADE);
621
+ if (bigint_data) {
622
+ bigint_data[idx] = DecadeOperator::DecadeFromYear(yyyy);
595
623
  }
596
- part_data = HasPartValue(part_values, DatePartSpecifier::CENTURY);
597
- if (part_data) {
598
- part_data[idx] = CenturyOperator::CenturyFromYear(yyyy);
624
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::CENTURY);
625
+ if (bigint_data) {
626
+ bigint_data[idx] = CenturyOperator::CenturyFromYear(yyyy);
599
627
  }
600
- part_data = HasPartValue(part_values, DatePartSpecifier::MILLENNIUM);
601
- if (part_data) {
602
- part_data[idx] = MillenniumOperator::MillenniumFromYear(yyyy);
628
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::MILLENNIUM);
629
+ if (bigint_data) {
630
+ bigint_data[idx] = MillenniumOperator::MillenniumFromYear(yyyy);
603
631
  }
604
- part_data = HasPartValue(part_values, DatePartSpecifier::QUARTER);
605
- if (part_data) {
606
- part_data[idx] = QuarterOperator::QuarterFromMonth(mm);
632
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::QUARTER);
633
+ if (bigint_data) {
634
+ bigint_data[idx] = QuarterOperator::QuarterFromMonth(mm);
607
635
  }
608
- part_data = HasPartValue(part_values, DatePartSpecifier::ERA);
609
- if (part_data) {
610
- part_data[idx] = EraOperator::EraFromYear(yyyy);
636
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::ERA);
637
+ if (bigint_data) {
638
+ bigint_data[idx] = EraOperator::EraFromYear(yyyy);
611
639
  }
612
640
  }
613
641
 
614
642
  // Week calculations
615
643
  if (mask & DOW) {
616
644
  auto isodow = Date::ExtractISODayOfTheWeek(input);
617
- part_data = HasPartValue(part_values, DatePartSpecifier::DOW);
618
- if (part_data) {
619
- part_data[idx] = DayOfWeekOperator::DayOfWeekFromISO(isodow);
645
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::DOW);
646
+ if (bigint_data) {
647
+ bigint_data[idx] = DayOfWeekOperator::DayOfWeekFromISO(isodow);
620
648
  }
621
- part_data = HasPartValue(part_values, DatePartSpecifier::ISODOW);
622
- if (part_data) {
623
- part_data[idx] = isodow;
649
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::ISODOW);
650
+ if (bigint_data) {
651
+ bigint_data[idx] = isodow;
624
652
  }
625
653
  }
626
654
 
@@ -629,30 +657,36 @@ struct DatePart {
629
657
  int32_t ww = 0;
630
658
  int32_t iyyy = 0;
631
659
  Date::ExtractISOYearWeek(input, iyyy, ww);
632
- part_data = HasPartValue(part_values, DatePartSpecifier::WEEK);
633
- if (part_data) {
634
- part_data[idx] = ww;
660
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::WEEK);
661
+ if (bigint_data) {
662
+ bigint_data[idx] = ww;
635
663
  }
636
- part_data = HasPartValue(part_values, DatePartSpecifier::ISOYEAR);
637
- if (part_data) {
638
- part_data[idx] = iyyy;
664
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::ISOYEAR);
665
+ if (bigint_data) {
666
+ bigint_data[idx] = iyyy;
639
667
  }
640
- part_data = HasPartValue(part_values, DatePartSpecifier::YEARWEEK);
641
- if (part_data) {
642
- part_data[idx] = YearWeekOperator::YearWeekFromParts(iyyy, ww);
668
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::YEARWEEK);
669
+ if (bigint_data) {
670
+ bigint_data[idx] = YearWeekOperator::YearWeekFromParts(iyyy, ww);
643
671
  }
644
672
  }
645
673
 
646
674
  if (mask & EPOCH) {
647
- part_data = HasPartValue(part_values, DatePartSpecifier::EPOCH);
648
- if (part_data) {
649
- part_data[idx] = Date::Epoch(input);
675
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::EPOCH);
676
+ if (bigint_data) {
677
+ bigint_data[idx] = Date::Epoch(input);
650
678
  }
651
679
  }
652
680
  if (mask & DOY) {
653
- part_data = HasPartValue(part_values, DatePartSpecifier::DOY);
654
- if (part_data) {
655
- part_data[idx] = Date::ExtractDayOfTheYear(input);
681
+ bigint_data = HasPartValue(bigint_values, DatePartSpecifier::DOY);
682
+ if (bigint_data) {
683
+ bigint_data[idx] = Date::ExtractDayOfTheYear(input);
684
+ }
685
+ }
686
+ if (mask & JD) {
687
+ auto double_data = HasPartValue(double_values, DatePartSpecifier::JULIAN_DAY);
688
+ if (double_data) {
689
+ double_data[idx] = Date::ExtractJulianDay(input);
656
690
  }
657
691
  }
658
692
  }
@@ -1046,35 +1080,50 @@ int64_t DatePart::TimezoneOperator::Operation(dtime_t input) {
1046
1080
  }
1047
1081
 
1048
1082
  template <>
1049
- void DatePart::StructOperator::Operation(int64_t **part_values, const dtime_t &input, const idx_t idx,
1050
- const part_mask_t mask) {
1083
+ double DatePart::JulianDayOperator::Operation(date_t input) {
1084
+ return Date::ExtractJulianDay(input);
1085
+ }
1086
+
1087
+ template <>
1088
+ double DatePart::JulianDayOperator::Operation(interval_t input) {
1089
+ throw NotImplementedException("interval units \"julian\" not recognized");
1090
+ }
1091
+
1092
+ template <>
1093
+ double DatePart::JulianDayOperator::Operation(dtime_t input) {
1094
+ throw NotImplementedException("\"time\" units \"julian\" not recognized");
1095
+ }
1096
+
1097
+ template <>
1098
+ void DatePart::StructOperator::Operation(bigint_vec &bigint_values, double_vec &double_values, const dtime_t &input,
1099
+ const idx_t idx, const part_mask_t mask) {
1051
1100
  int64_t *part_data;
1052
1101
  if (mask & TIME) {
1053
1102
  const auto micros = MicrosecondsOperator::Operation<dtime_t, int64_t>(input);
1054
- part_data = HasPartValue(part_values, DatePartSpecifier::MICROSECONDS);
1103
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MICROSECONDS);
1055
1104
  if (part_data) {
1056
1105
  part_data[idx] = micros;
1057
1106
  }
1058
- part_data = HasPartValue(part_values, DatePartSpecifier::MILLISECONDS);
1107
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MILLISECONDS);
1059
1108
  if (part_data) {
1060
1109
  part_data[idx] = micros / Interval::MICROS_PER_MSEC;
1061
1110
  }
1062
- part_data = HasPartValue(part_values, DatePartSpecifier::SECOND);
1111
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::SECOND);
1063
1112
  if (part_data) {
1064
1113
  part_data[idx] = micros / Interval::MICROS_PER_SEC;
1065
1114
  }
1066
- part_data = HasPartValue(part_values, DatePartSpecifier::MINUTE);
1115
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MINUTE);
1067
1116
  if (part_data) {
1068
1117
  part_data[idx] = MinutesOperator::Operation<dtime_t, int64_t>(input);
1069
1118
  }
1070
- part_data = HasPartValue(part_values, DatePartSpecifier::HOUR);
1119
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::HOUR);
1071
1120
  if (part_data) {
1072
1121
  part_data[idx] = HoursOperator::Operation<dtime_t, int64_t>(input);
1073
1122
  }
1074
1123
  }
1075
1124
 
1076
1125
  if (mask & EPOCH) {
1077
- part_data = HasPartValue(part_values, DatePartSpecifier::EPOCH);
1126
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::EPOCH);
1078
1127
  if (part_data) {
1079
1128
  part_data[idx] = EpochOperator::Operation<dtime_t, int64_t>(input);
1080
1129
  ;
@@ -1082,15 +1131,15 @@ void DatePart::StructOperator::Operation(int64_t **part_values, const dtime_t &i
1082
1131
  }
1083
1132
 
1084
1133
  if (mask & ZONE) {
1085
- part_data = HasPartValue(part_values, DatePartSpecifier::TIMEZONE);
1134
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::TIMEZONE);
1086
1135
  if (part_data) {
1087
1136
  part_data[idx] = 0;
1088
1137
  }
1089
- part_data = HasPartValue(part_values, DatePartSpecifier::TIMEZONE_HOUR);
1138
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::TIMEZONE_HOUR);
1090
1139
  if (part_data) {
1091
1140
  part_data[idx] = 0;
1092
1141
  }
1093
- part_data = HasPartValue(part_values, DatePartSpecifier::TIMEZONE_MINUTE);
1142
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::TIMEZONE_MINUTE);
1094
1143
  if (part_data) {
1095
1144
  part_data[idx] = 0;
1096
1145
  }
@@ -1098,56 +1147,63 @@ void DatePart::StructOperator::Operation(int64_t **part_values, const dtime_t &i
1098
1147
  }
1099
1148
 
1100
1149
  template <>
1101
- void DatePart::StructOperator::Operation(int64_t **part_values, const timestamp_t &input, const idx_t idx,
1102
- const part_mask_t mask) {
1150
+ void DatePart::StructOperator::Operation(bigint_vec &bigint_values, double_vec &double_values, const timestamp_t &input,
1151
+ const idx_t idx, const part_mask_t mask) {
1103
1152
  date_t d;
1104
1153
  dtime_t t;
1105
1154
  Timestamp::Convert(input, d, t);
1106
1155
 
1107
1156
  // Both define epoch, and the correct value is the sum.
1108
1157
  // So mask it out and compute it separately.
1109
- Operation(part_values, d, idx, mask & ~EPOCH);
1110
- Operation(part_values, t, idx, mask & ~EPOCH);
1158
+ Operation(bigint_values, double_values, d, idx, mask & ~EPOCH);
1159
+ Operation(bigint_values, double_values, t, idx, mask & ~EPOCH);
1111
1160
 
1112
1161
  if (mask & EPOCH) {
1113
- auto part_data = HasPartValue(part_values, DatePartSpecifier::EPOCH);
1162
+ auto part_data = HasPartValue(bigint_values, DatePartSpecifier::EPOCH);
1114
1163
  if (part_data) {
1115
1164
  part_data[idx] = EpochOperator::Operation<timestamp_t, int64_t>(input);
1116
1165
  }
1117
1166
  }
1167
+
1168
+ if (mask & JD) {
1169
+ auto part_data = HasPartValue(double_values, DatePartSpecifier::JULIAN_DAY);
1170
+ if (part_data) {
1171
+ part_data[idx] = JulianDayOperator::Operation<timestamp_t, double>(input);
1172
+ }
1173
+ }
1118
1174
  }
1119
1175
 
1120
1176
  template <>
1121
- void DatePart::StructOperator::Operation(int64_t **part_values, const interval_t &input, const idx_t idx,
1122
- const part_mask_t mask) {
1177
+ void DatePart::StructOperator::Operation(bigint_vec &bigint_values, double_vec &double_values, const interval_t &input,
1178
+ const idx_t idx, const part_mask_t mask) {
1123
1179
  int64_t *part_data;
1124
1180
  if (mask & YMD) {
1125
1181
  const auto mm = input.months % Interval::MONTHS_PER_YEAR;
1126
- part_data = HasPartValue(part_values, DatePartSpecifier::YEAR);
1182
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::YEAR);
1127
1183
  if (part_data) {
1128
1184
  part_data[idx] = input.months / Interval::MONTHS_PER_YEAR;
1129
1185
  }
1130
- part_data = HasPartValue(part_values, DatePartSpecifier::MONTH);
1186
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MONTH);
1131
1187
  if (part_data) {
1132
1188
  part_data[idx] = mm;
1133
1189
  }
1134
- part_data = HasPartValue(part_values, DatePartSpecifier::DAY);
1190
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::DAY);
1135
1191
  if (part_data) {
1136
1192
  part_data[idx] = input.days;
1137
1193
  }
1138
- part_data = HasPartValue(part_values, DatePartSpecifier::DECADE);
1194
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::DECADE);
1139
1195
  if (part_data) {
1140
1196
  part_data[idx] = input.months / Interval::MONTHS_PER_DECADE;
1141
1197
  }
1142
- part_data = HasPartValue(part_values, DatePartSpecifier::CENTURY);
1198
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::CENTURY);
1143
1199
  if (part_data) {
1144
1200
  part_data[idx] = input.months / Interval::MONTHS_PER_CENTURY;
1145
1201
  }
1146
- part_data = HasPartValue(part_values, DatePartSpecifier::MILLENNIUM);
1202
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MILLENNIUM);
1147
1203
  if (part_data) {
1148
1204
  part_data[idx] = input.months / Interval::MONTHS_PER_MILLENIUM;
1149
1205
  }
1150
- part_data = HasPartValue(part_values, DatePartSpecifier::QUARTER);
1206
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::QUARTER);
1151
1207
  if (part_data) {
1152
1208
  part_data[idx] = mm / Interval::MONTHS_PER_QUARTER + 1;
1153
1209
  }
@@ -1155,30 +1211,30 @@ void DatePart::StructOperator::Operation(int64_t **part_values, const interval_t
1155
1211
 
1156
1212
  if (mask & TIME) {
1157
1213
  const auto micros = MicrosecondsOperator::Operation<interval_t, int64_t>(input);
1158
- part_data = HasPartValue(part_values, DatePartSpecifier::MICROSECONDS);
1214
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MICROSECONDS);
1159
1215
  if (part_data) {
1160
1216
  part_data[idx] = micros;
1161
1217
  }
1162
- part_data = HasPartValue(part_values, DatePartSpecifier::MILLISECONDS);
1218
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MILLISECONDS);
1163
1219
  if (part_data) {
1164
1220
  part_data[idx] = micros / Interval::MICROS_PER_MSEC;
1165
1221
  }
1166
- part_data = HasPartValue(part_values, DatePartSpecifier::SECOND);
1222
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::SECOND);
1167
1223
  if (part_data) {
1168
1224
  part_data[idx] = micros / Interval::MICROS_PER_SEC;
1169
1225
  }
1170
- part_data = HasPartValue(part_values, DatePartSpecifier::MINUTE);
1226
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::MINUTE);
1171
1227
  if (part_data) {
1172
1228
  part_data[idx] = MinutesOperator::Operation<interval_t, int64_t>(input);
1173
1229
  }
1174
- part_data = HasPartValue(part_values, DatePartSpecifier::HOUR);
1230
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::HOUR);
1175
1231
  if (part_data) {
1176
1232
  part_data[idx] = HoursOperator::Operation<interval_t, int64_t>(input);
1177
1233
  }
1178
1234
  }
1179
1235
 
1180
1236
  if (mask & EPOCH) {
1181
- part_data = HasPartValue(part_values, DatePartSpecifier::EPOCH);
1237
+ part_data = HasPartValue(bigint_values, DatePartSpecifier::EPOCH);
1182
1238
  if (part_data) {
1183
1239
  part_data[idx] = EpochOperator::Operation<interval_t, int64_t>(input);
1184
1240
  }
@@ -1254,6 +1310,45 @@ static void DatePartFunction(DataChunk &args, ExpressionState &state, Vector &re
1254
1310
  });
1255
1311
  }
1256
1312
 
1313
+ static unique_ptr<FunctionData> DatePartBind(ClientContext &context, ScalarFunction &bound_function,
1314
+ vector<unique_ptr<Expression>> &arguments) {
1315
+ // If we are only looking for Julian Days for timestamps,
1316
+ // then return doubles.
1317
+ if (arguments[0]->HasParameter() || !arguments[0]->IsFoldable()) {
1318
+ return nullptr;
1319
+ }
1320
+
1321
+ Value part_value = ExpressionExecutor::EvaluateScalar(context, *arguments[0]);
1322
+ if (part_value.IsNull()) {
1323
+ return nullptr;
1324
+ }
1325
+ const auto part_name = part_value.ToString();
1326
+ switch (GetDatePartSpecifier(part_name)) {
1327
+ case DatePartSpecifier::JULIAN_DAY:
1328
+ arguments.erase(arguments.begin());
1329
+ bound_function.arguments.erase(bound_function.arguments.begin());
1330
+ bound_function.name = "julian";
1331
+ bound_function.return_type = LogicalType::DOUBLE;
1332
+ switch (arguments[0]->return_type.id()) {
1333
+ case LogicalType::TIMESTAMP:
1334
+ bound_function.function = DatePart::UnaryFunction<timestamp_t, double, DatePart::JulianDayOperator>;
1335
+ bound_function.statistics = DatePart::JulianDayOperator::template PropagateStatistics<timestamp_t>;
1336
+ break;
1337
+ case LogicalType::DATE:
1338
+ bound_function.function = DatePart::UnaryFunction<date_t, double, DatePart::JulianDayOperator>;
1339
+ bound_function.statistics = DatePart::JulianDayOperator::template PropagateStatistics<date_t>;
1340
+ break;
1341
+ default:
1342
+ throw BinderException("%s can only take DATE or TIMESTAMP arguments", bound_function.name);
1343
+ }
1344
+ break;
1345
+ default:
1346
+ break;
1347
+ }
1348
+
1349
+ return nullptr;
1350
+ }
1351
+
1257
1352
  ScalarFunctionSet GetGenericDatePartFunction(scalar_function_t date_func, scalar_function_t ts_func,
1258
1353
  scalar_function_t interval_func, function_statistics_t date_stats,
1259
1354
  function_statistics_t ts_stats) {
@@ -1332,6 +1427,10 @@ struct DayNameOperator {
1332
1427
  struct StructDatePart {
1333
1428
  using part_codes_t = vector<DatePartSpecifier>;
1334
1429
 
1430
+ static const auto BEGIN_BIGINT = size_t(DatePartSpecifier::YEAR);
1431
+ static const auto BEGIN_DOUBLE = size_t(DatePartSpecifier::JULIAN_DAY);
1432
+ static const auto BEGIN_INVALID = size_t(DatePartSpecifier::JULIAN_DAY) + 1;
1433
+
1335
1434
  struct BindData : public VariableReturnBindData {
1336
1435
  part_codes_t part_codes;
1337
1436
 
@@ -1375,7 +1474,8 @@ struct StructDatePart {
1375
1474
  }
1376
1475
  name_collision_set.insert(part_name);
1377
1476
  part_codes.emplace_back(part_code);
1378
- struct_children.emplace_back(make_pair(part_name, LogicalType::BIGINT));
1477
+ const auto part_type = IsBigintDatepart(part_code) ? LogicalType::BIGINT : LogicalType::DOUBLE;
1478
+ struct_children.emplace_back(make_pair(part_name, part_type));
1379
1479
  }
1380
1480
  } else {
1381
1481
  throw BinderException("%s can only take constant lists of part names", bound_function.name);
@@ -1394,14 +1494,15 @@ struct StructDatePart {
1394
1494
 
1395
1495
  const auto count = args.size();
1396
1496
  Vector &input = args.data[0];
1397
- vector<int64_t *> part_values(int(DatePartSpecifier::TIMEZONE_MINUTE) + 1, nullptr);
1497
+ DatePart::StructOperator::bigint_vec bigint_values(size_t(BEGIN_DOUBLE), nullptr);
1498
+ DatePart::StructOperator::double_vec double_values(BEGIN_INVALID - size_t(BEGIN_DOUBLE), nullptr);
1398
1499
  const auto part_mask = DatePart::StructOperator::GetMask(info.part_codes);
1399
1500
 
1400
1501
  auto &child_entries = StructVector::GetEntries(result);
1401
1502
 
1402
1503
  // The first computer of a part "owns" it
1403
1504
  // and other requestors just reference the owner
1404
- vector<size_t> owners(int(DatePartSpecifier::TIMEZONE_MINUTE) + 1, child_entries.size());
1505
+ vector<size_t> owners(int(DatePartSpecifier::JULIAN_DAY) + 1, child_entries.size());
1405
1506
  for (size_t col = 0; col < child_entries.size(); ++col) {
1406
1507
  const auto part_index = size_t(info.part_codes[col]);
1407
1508
  if (owners[part_index] == child_entries.size()) {
@@ -1421,12 +1522,16 @@ struct StructDatePart {
1421
1522
  ConstantVector::SetNull(*child_entry, false);
1422
1523
  const auto part_index = size_t(info.part_codes[col]);
1423
1524
  if (owners[part_index] == col) {
1424
- part_values[part_index] = ConstantVector::GetData<int64_t>(*child_entry);
1525
+ if (IsBigintDatepart(info.part_codes[col])) {
1526
+ bigint_values[part_index - BEGIN_BIGINT] = ConstantVector::GetData<int64_t>(*child_entry);
1527
+ } else {
1528
+ double_values[part_index - BEGIN_DOUBLE] = ConstantVector::GetData<double>(*child_entry);
1529
+ }
1425
1530
  }
1426
1531
  }
1427
1532
  auto tdata = ConstantVector::GetData<INPUT_TYPE>(input);
1428
1533
  if (Value::IsFinite(tdata[0])) {
1429
- DatePart::StructOperator::Operation(part_values.data(), tdata[0], 0, part_mask);
1534
+ DatePart::StructOperator::Operation(bigint_values, double_values, tdata[0], 0, part_mask);
1430
1535
  } else {
1431
1536
  for (auto &child_entry : child_entries) {
1432
1537
  ConstantVector::SetNull(*child_entry, true);
@@ -1459,7 +1564,11 @@ struct StructDatePart {
1459
1564
  // Pre-multiplex
1460
1565
  const auto part_index = size_t(info.part_codes[col]);
1461
1566
  if (owners[part_index] == col) {
1462
- part_values[part_index] = FlatVector::GetData<int64_t>(*child_entry);
1567
+ if (IsBigintDatepart(info.part_codes[col])) {
1568
+ bigint_values[part_index - BEGIN_BIGINT] = FlatVector::GetData<int64_t>(*child_entry);
1569
+ } else {
1570
+ double_values[part_index - BEGIN_DOUBLE] = FlatVector::GetData<double>(*child_entry);
1571
+ }
1463
1572
  }
1464
1573
  }
1465
1574
 
@@ -1467,7 +1576,7 @@ struct StructDatePart {
1467
1576
  const auto idx = rdata.sel->get_index(i);
1468
1577
  if (arg_valid.RowIsValid(idx)) {
1469
1578
  if (Value::IsFinite(tdata[idx])) {
1470
- DatePart::StructOperator::Operation(part_values.data(), tdata[idx], i, part_mask);
1579
+ DatePart::StructOperator::Operation(bigint_values, double_values, tdata[idx], i, part_mask);
1471
1580
  } else {
1472
1581
  for (auto &child_entry : child_entries) {
1473
1582
  FlatVector::Validity(*child_entry).SetInvalid(i);
@@ -1695,16 +1804,32 @@ ScalarFunctionSet DayNameFun::GetFunctions() {
1695
1804
  return dayname;
1696
1805
  }
1697
1806
 
1807
+ ScalarFunctionSet JulianDayFun::GetFunctions() {
1808
+ using OP = DatePart::JulianDayOperator;
1809
+
1810
+ ScalarFunctionSet operator_set;
1811
+ auto date_func = DatePart::UnaryFunction<date_t, double, OP>;
1812
+ auto date_stats = OP::template PropagateStatistics<date_t>;
1813
+ operator_set.AddFunction(
1814
+ ScalarFunction({LogicalType::DATE}, LogicalType::DOUBLE, date_func, nullptr, nullptr, date_stats));
1815
+ auto ts_func = DatePart::UnaryFunction<timestamp_t, double, OP>;
1816
+ auto ts_stats = OP::template PropagateStatistics<timestamp_t>;
1817
+ operator_set.AddFunction(
1818
+ ScalarFunction({LogicalType::TIMESTAMP}, LogicalType::DOUBLE, ts_func, nullptr, nullptr, ts_stats));
1819
+
1820
+ return operator_set;
1821
+ }
1822
+
1698
1823
  ScalarFunctionSet DatePartFun::GetFunctions() {
1699
1824
  ScalarFunctionSet date_part;
1700
- date_part.AddFunction(
1701
- ScalarFunction({LogicalType::VARCHAR, LogicalType::DATE}, LogicalType::BIGINT, DatePartFunction<date_t>));
1825
+ date_part.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::DATE}, LogicalType::BIGINT,
1826
+ DatePartFunction<date_t>, DatePartBind));
1702
1827
  date_part.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::TIMESTAMP}, LogicalType::BIGINT,
1703
- DatePartFunction<timestamp_t>));
1704
- date_part.AddFunction(
1705
- ScalarFunction({LogicalType::VARCHAR, LogicalType::TIME}, LogicalType::BIGINT, DatePartFunction<dtime_t>));
1828
+ DatePartFunction<timestamp_t>, DatePartBind));
1829
+ date_part.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::TIME}, LogicalType::BIGINT,
1830
+ DatePartFunction<dtime_t>, DatePartBind));
1706
1831
  date_part.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::INTERVAL}, LogicalType::BIGINT,
1707
- DatePartFunction<interval_t>));
1832
+ DatePartFunction<interval_t>, DatePartBind));
1708
1833
 
1709
1834
  // struct variants
1710
1835
  date_part.AddFunction(StructDatePart::GetFunction<date_t>(LogicalType::DATE));
@@ -322,6 +322,7 @@ static int64_t SubtractDateParts(DatePartSpecifier type, TA startdate, TB enddat
322
322
  case DatePartSpecifier::DOW:
323
323
  case DatePartSpecifier::ISODOW:
324
324
  case DatePartSpecifier::DOY:
325
+ case DatePartSpecifier::JULIAN_DAY:
325
326
  return DateSub::DayOperator::template Operation<TA, TB, TR>(startdate, enddate);
326
327
  case DatePartSpecifier::DECADE:
327
328
  return DateSub::DecadeOperator::template Operation<TA, TB, TR>(startdate, enddate);
@@ -376,6 +377,7 @@ static void DateSubBinaryExecutor(DatePartSpecifier type, Vector &left, Vector &
376
377
  case DatePartSpecifier::DOW:
377
378
  case DatePartSpecifier::ISODOW:
378
379
  case DatePartSpecifier::DOY:
380
+ case DatePartSpecifier::JULIAN_DAY:
379
381
  DateSub::BinaryExecute<TA, TB, TR, DateSub::DayOperator>(left, right, result, count);
380
382
  break;
381
383
  case DatePartSpecifier::DECADE:
@@ -485,6 +485,7 @@ static TR TruncateElement(DatePartSpecifier type, TA element) {
485
485
  case DatePartSpecifier::DOW:
486
486
  case DatePartSpecifier::ISODOW:
487
487
  case DatePartSpecifier::DOY:
488
+ case DatePartSpecifier::JULIAN_DAY:
488
489
  return DateTrunc::DayOperator::Operation<TA, TR>(element);
489
490
  case DatePartSpecifier::HOUR:
490
491
  return DateTrunc::HourOperator::Operation<TA, TR>(element);
@@ -541,6 +542,7 @@ static void DateTruncUnaryExecutor(DatePartSpecifier type, Vector &left, Vector
541
542
  case DatePartSpecifier::DOW:
542
543
  case DatePartSpecifier::ISODOW:
543
544
  case DatePartSpecifier::DOY:
545
+ case DatePartSpecifier::JULIAN_DAY:
544
546
  DateTrunc::UnaryExecute<TA, TR, DateTrunc::DayOperator>(left, result, count);
545
547
  break;
546
548
  case DatePartSpecifier::HOUR:
@@ -641,6 +643,7 @@ static function_statistics_t DateTruncStats(DatePartSpecifier type) {
641
643
  case DatePartSpecifier::DOW:
642
644
  case DatePartSpecifier::ISODOW:
643
645
  case DatePartSpecifier::DOY:
646
+ case DatePartSpecifier::JULIAN_DAY:
644
647
  return PropagateDateTruncStatistics<TA, TR, DateTrunc::DayOperator>;
645
648
  case DatePartSpecifier::HOUR:
646
649
  return PropagateDateTruncStatistics<TA, TR, DateTrunc::HourOperator>;
@@ -685,6 +688,7 @@ static unique_ptr<FunctionData> DateTruncBind(ClientContext &context, ScalarFunc
685
688
  case DatePartSpecifier::DOW:
686
689
  case DatePartSpecifier::ISODOW:
687
690
  case DatePartSpecifier::DOY:
691
+ case DatePartSpecifier::JULIAN_DAY:
688
692
  switch (bound_function.arguments[1].id()) {
689
693
  case LogicalType::TIMESTAMP:
690
694
  bound_function.function = DateTruncFunction<timestamp_t, date_t>;
@@ -1,8 +1,8 @@
1
1
  #ifndef DUCKDB_VERSION
2
- #define DUCKDB_VERSION "0.8.2-dev3092"
2
+ #define DUCKDB_VERSION "0.8.2-dev3190"
3
3
  #endif
4
4
  #ifndef DUCKDB_SOURCE_ID
5
- #define DUCKDB_SOURCE_ID "1f17e1cec6"
5
+ #define DUCKDB_SOURCE_ID "3196df79ce"
6
6
  #endif
7
7
  #include "duckdb/function/table/system_functions.hpp"
8
8
  #include "duckdb/main/database.hpp"
@@ -13,6 +13,7 @@
13
13
  namespace duckdb {
14
14
 
15
15
  enum class DatePartSpecifier : uint8_t {
16
+ // BIGINT values
16
17
  YEAR,
17
18
  MONTH,
18
19
  DAY,
@@ -35,9 +36,16 @@ enum class DatePartSpecifier : uint8_t {
35
36
  ERA,
36
37
  TIMEZONE,
37
38
  TIMEZONE_HOUR,
38
- TIMEZONE_MINUTE
39
+ TIMEZONE_MINUTE,
40
+
41
+ // DOUBLE values
42
+ JULIAN_DAY
39
43
  };
40
44
 
45
+ inline bool IsBigintDatepart(DatePartSpecifier part_code) {
46
+ return size_t(part_code) < size_t(DatePartSpecifier::JULIAN_DAY);
47
+ }
48
+
41
49
  DUCKDB_API bool TryGetDatePartSpecifier(const string &specifier, DatePartSpecifier &result);
42
50
  DUCKDB_API DatePartSpecifier GetDatePartSpecifier(const string &specifier);
43
51
 
@@ -181,6 +181,8 @@ public:
181
181
  DUCKDB_API static int32_t ExtractISODayOfTheWeek(date_t date);
182
182
  //! Extract the day of the year
183
183
  DUCKDB_API static int32_t ExtractDayOfTheYear(date_t date);
184
+ //! Extract the day of the year
185
+ DUCKDB_API static int64_t ExtractJulianDay(date_t date);
184
186
  //! Extract the ISO week number
185
187
  //! ISO weeks start on Monday and the first week of a year
186
188
  //! contains January 4 of that year.
@@ -151,6 +151,8 @@ public:
151
151
  DUCKDB_API static int64_t GetEpochMicroSeconds(timestamp_t timestamp);
152
152
  //! Convert a timestamp to epoch (in nanoseconds)
153
153
  DUCKDB_API static int64_t GetEpochNanoSeconds(timestamp_t timestamp);
154
+ //! Convert a timestamp to a Julian Day
155
+ DUCKDB_API static double GetJulianDay(timestamp_t timestamp);
154
156
 
155
157
  DUCKDB_API static bool TryParseUTCOffset(const char *str, idx_t &pos, idx_t len, int &hour_offset,
156
158
  int &minute_offset);
@@ -264,6 +264,15 @@ struct ISOYearFun {
264
264
  static ScalarFunctionSet GetFunctions();
265
265
  };
266
266
 
267
+ struct JulianDayFun {
268
+ static constexpr const char *Name = "julian";
269
+ static constexpr const char *Parameters = "ts";
270
+ static constexpr const char *Description = "Extract the Julian Day number from a date or timestamp";
271
+ static constexpr const char *Example = "julian(timestamp '2006-01-01 12:00')";
272
+
273
+ static ScalarFunctionSet GetFunctions();
274
+ };
275
+
267
276
  struct LastDayFun {
268
277
  static constexpr const char *Name = "last_day";
269
278
  static constexpr const char *Parameters = "ts";
@@ -37,12 +37,23 @@ static void ComputeSHA256String(const std::string &to_hash, std::string *res) {
37
37
  }
38
38
 
39
39
  static void ComputeSHA256FileSegment(FileHandle *handle, const idx_t start, const idx_t end, std::string *res) {
40
- const idx_t len = end - start;
41
- string file_content;
42
- file_content.resize(len);
43
- handle->Read((void *)file_content.data(), len, start);
40
+ idx_t iter = start;
41
+ const idx_t segment_size = 1024 * 8;
44
42
 
45
- ComputeSHA256String(file_content, res);
43
+ duckdb_mbedtls::MbedTlsWrapper::SHA256State state;
44
+
45
+ std::string to_hash;
46
+ while (iter < end) {
47
+ idx_t len = std::min(end - iter, segment_size);
48
+ to_hash.resize(len);
49
+ handle->Read((void *)to_hash.data(), len, iter);
50
+
51
+ state.AddString(to_hash);
52
+
53
+ iter += segment_size;
54
+ }
55
+
56
+ *res = state.Finalize();
46
57
  }
47
58
  #endif
48
59
 
@@ -142,12 +142,10 @@ bool BoundCastExpression::CastIsInvertible(const LogicalType &source_type, const
142
142
  }
143
143
  if (source_type.id() == LogicalTypeId::VARCHAR) {
144
144
  switch (target_type.id()) {
145
- case LogicalTypeId::TIME:
146
145
  case LogicalTypeId::TIMESTAMP:
147
146
  case LogicalTypeId::TIMESTAMP_NS:
148
147
  case LogicalTypeId::TIMESTAMP_MS:
149
148
  case LogicalTypeId::TIMESTAMP_SEC:
150
- case LogicalTypeId::TIME_TZ:
151
149
  case LogicalTypeId::TIMESTAMP_TZ:
152
150
  return true;
153
151
  default:
@@ -19,5 +19,15 @@ public:
19
19
  static void Hmac256(const char* key, size_t key_len, const char* message, size_t message_len, char* out);
20
20
 
21
21
  static constexpr size_t SHA256_HASH_BYTES = 32;
22
+
23
+ class SHA256State {
24
+ public:
25
+ SHA256State();
26
+ ~SHA256State();
27
+ void AddString(const std::string & str);
28
+ std::string Finalize();
29
+ private:
30
+ void *sha_context;
31
+ };
22
32
  };
23
33
  }
@@ -81,4 +81,34 @@ void MbedTlsWrapper::Hmac256(const char* key, size_t key_len, const char* messag
81
81
  throw runtime_error("HMAC256 Error");
82
82
  }
83
83
  mbedtls_md_free(&hmac_ctx);
84
- }
84
+ }
85
+
86
+ MbedTlsWrapper::SHA256State::SHA256State() : sha_context(new mbedtls_sha256_context()) {
87
+ mbedtls_sha256_init((mbedtls_sha256_context*)sha_context);
88
+
89
+ if (mbedtls_sha256_starts((mbedtls_sha256_context*)sha_context, false)) {
90
+ throw std::runtime_error("SHA256 Error");
91
+ }
92
+ }
93
+
94
+ MbedTlsWrapper::SHA256State::~SHA256State() {
95
+ mbedtls_sha256_free((mbedtls_sha256_context*)sha_context);
96
+ delete (mbedtls_sha256_context*)sha_context;
97
+ }
98
+
99
+ void MbedTlsWrapper::SHA256State::AddString(const std::string & str) {
100
+ if (mbedtls_sha256_update((mbedtls_sha256_context*)sha_context, (unsigned char*)str.data(), str.size())) {
101
+ throw std::runtime_error("SHA256 Error");
102
+ }
103
+ }
104
+
105
+ std::string MbedTlsWrapper::SHA256State::Finalize() {
106
+ string hash;
107
+ hash.resize(MbedTlsWrapper::SHA256_HASH_BYTES);
108
+
109
+ if (mbedtls_sha256_finish((mbedtls_sha256_context*)sha_context, (unsigned char*)hash.data())) {
110
+ throw std::runtime_error("SHA256 Error");
111
+ }
112
+
113
+ return hash;
114
+ }