Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct KahanAvgState {
void Combine(const KahanAvgState &other) {
this->count += other.count;
KahanAddInternal(other.value, this->value, this->err);
KahanAddInternal(other.err, this->value, this->err);
KahanAddInternal(-other.err, this->value, this->err);
}
};

Expand Down Expand Up @@ -143,6 +143,27 @@ struct DiscreteAverageOperation : public BaseSumOperation<AverageSetOperation, A
}
};

//! The average of TIME values is within a day - reject imported states that average outside of it
void ValidateTimeAverageStates(const_data_ptr_t states, idx_t count, idx_t stride) {
for (idx_t i = 0; i < count; i++) {
const auto state = Load<AvgState<hugeint_t>>(states + i * stride);
if (state.count == 0) {
continue;
}
const auto max_value = hugeint_t(0, state.count) * hugeint_t(Interval::MICROS_PER_DAY);
if (state.value < 0 || state.value > max_value) {
throw InvalidInputException("Invalid avg state - the average of %s over %llu values is not a valid time",
state.value.ToString(), state.count);
}
}
}

AggregateStateLayout TimeAverageStateLayout(AggregateLayoutInput &input) {
auto layout = AggregateFunction::StructStateLayout<AvgState<hugeint_t>>(input);
layout.validate_state = ValidateTimeAverageStates;
return layout;
}

struct HugeintAverageOperation : public BaseSumOperation<AverageSetOperation, HugeintAdd> {
template <class STATE, class OP>
static void RepeatedCombine(const STATE &source, STATE &target, AggregateInputData &input, idx_t count) {
Expand Down Expand Up @@ -182,7 +203,7 @@ struct KahanAverageOperation : public BaseSumOperation<AverageSetOperation, Kaha
if (state.count == 0) {
finalize_data.ReturnNull();
} else {
target = (state.value / state.count) + (state.err / state.count);
target = (state.value / state.count) - (state.err / state.count);
}
}
};
Expand Down Expand Up @@ -343,12 +364,14 @@ AggregateFunctionSet AvgFun::GetFunctions() {
auto time_avg = AggregateFunction::UnaryAggregate<AvgState<hugeint_t>, int64_t, int64_t, DiscreteAverageOperation>(
LogicalType::TIME, LogicalType::TIME);
time_avg.GetSignature().GetParameter(0).SetName("x");
time_avg.SetStructStateExport(TimeAverageStateLayout);
avg.AddFunction(time_avg);

auto time_tz_avg =
AggregateFunction::UnaryAggregate<AvgState<hugeint_t>, dtime_tz_t, dtime_tz_t, TimeTZAverageOperation>(
LogicalType::TIME_TZ, LogicalType::TIME_TZ);
time_tz_avg.GetSignature().GetParameter(0).SetName("x");
time_tz_avg.SetStructStateExport(TimeAverageStateLayout);
avg.AddFunction(time_tz_avg);

return avg;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,23 @@ struct HugeintSumOperation
}
};

//! The sum of a DECIMAL is a DECIMAL(38) - finalizing throws if the sum does not fit in it
template <class OP>
struct DecimalSumOperation : public OP {
template <class T, class STATE>
static void Finalize(STATE &state, T &target, AggregateFinalizeData &finalize_data) {
OP::template Finalize<T, STATE>(state, target, finalize_data);
if (!state.is_set) {
return;
}
const auto &max_value = Hugeint::POWERS_OF_TEN[Decimal::MAX_WIDTH_DECIMAL];
if (target >= max_value || target <= -max_value) {
throw OutOfRangeException("Overflow in SUM of DECIMAL: the result does not fit in DECIMAL(%d)",
Decimal::MAX_WIDTH_DECIMAL);
}
}
};

unique_ptr<FunctionData> SumNoOverflowBind(BindAggregateFunctionInput &input) {
throw BinderException("sum_no_overflow is for internal use only!");
}
Expand Down Expand Up @@ -416,11 +433,36 @@ AggregateFunction GetSumAggregate(PhysicalType type) {
}
}

template <class INPUT_TYPE, class OP>
AggregateFunction GetDecimalSumAggregate(const LogicalType &input_type) {
auto function =
AggregateFunction::UnaryAggregate<SumState<hugeint_t>, INPUT_TYPE, hugeint_t, DecimalSumOperation<OP>>(
input_type, LogicalType::HUGEINT);
function.GetSignature().GetParameter(0).SetName("arg");
function.SetStatisticsCallback(SumPropagateStats);
function.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT);
return function;
}

AggregateFunction GetDecimalSumAggregate(PhysicalType type) {
switch (type) {
case PhysicalType::INT32:
return GetDecimalSumAggregate<int32_t, SumToHugeintOperation>(LogicalType::INTEGER);
case PhysicalType::INT64:
return GetDecimalSumAggregate<int64_t, SumToHugeintOperation>(LogicalType::BIGINT);
case PhysicalType::INT128:
return GetDecimalSumAggregate<hugeint_t, HugeintSumOperation>(LogicalType::HUGEINT);
default:
// the sum of smaller decimals is accumulated in an int64_t, which always fits in a DECIMAL(38)
return GetSumAggregate(type);
}
}

unique_ptr<FunctionData> BindDecimalSum(BindAggregateFunctionInput &input) {
auto &function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
auto decimal_type = arguments[0]->GetReturnType();
function.ReplaceImplementation(GetSumAggregate(decimal_type.InternalType()));
function.ReplaceImplementation(GetDecimalSumAggregate(decimal_type.InternalType()));
function.SetName("sum");
function.GetArguments()[0] = decimal_type;
function.SetReturnType(LogicalType::DECIMAL(Decimal::MAX_WIDTH_DECIMAL, DecimalType::GetScale(decimal_type)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,30 @@ void ApproxQuantileImportState(AggregateImportInputData &input) {
}
arena_vector<duckdb_tdigest::Centroid> centroids(input.allocator);
centroids.reserve(centroid_list.GetListLength());
double total_weight = 0;
for (const auto centroid_entry : centroid_list.GetChildValues()) {
const auto mean_entry = centroid_entry.template GetChildValue<0>();
const auto weight_entry = centroid_entry.template GetChildValue<1>();
if (!centroid_entry.IsValid() || !mean_entry.IsValid() || !weight_entry.IsValid()) {
throw InvalidInputException("Invalid approx_quantile state - the centroids cannot be NULL");
}
centroids.emplace_back(mean_entry.GetValue(), weight_entry.GetValue());
const auto mean = mean_entry.GetValue();
const auto weight = weight_entry.GetValue();
if (Value::IsNan(mean) || !Value::IsFinite(weight) || weight <= 0) {
throw InvalidInputException(
"Invalid approx_quantile state - centroids must have a mean and a positive finite weight");
}
if (!centroids.empty() && mean < centroids.back().mean()) {
throw InvalidInputException("Invalid approx_quantile state - centroids must be sorted by their mean");
}
total_weight += weight;
centroids.emplace_back(mean, weight);
}
if (!Value::IsFinite(total_weight)) {
throw InvalidInputException("Invalid approx_quantile state - the total centroid weight must be finite");
}
if (Value::IsNan(min_entry.GetValue()) || Value::IsNan(max_entry.GetValue())) {
throw InvalidInputException("Invalid approx_quantile state - min and max cannot be NaN");
}
auto digest = input.allocator.Make<duckdb_tdigest::TDigest>(
std::move(centroids), duckdb::arena_vector<duckdb_tdigest::Centroid>(input.allocator), 100, 0, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ struct QuantileListFallback : QuantileOperation {
//===--------------------------------------------------------------------===//
template <class OP>
AggregateFunction GetDiscreteQuantileTemplated(const LogicalType &type) {
if (type.id() == LogicalTypeId::TIME_TZ) {
// TIMETZ is ordered by its normalized time, not by its raw bits
return OP::template GetFunction<dtime_tz_t>(type);
}
switch (type.InternalType()) {
#if !DUCKDB_SMALLER_BINARY(quantile_types)
case PhysicalType::INT8:
Expand Down Expand Up @@ -550,8 +554,10 @@ AggregateFunction GetContinuousQuantileTemplated(const LogicalType &type) {
case LogicalTypeId::TIMESTAMP_NS:
return OP::template GetFunction<timestamp_t, timestamp_t>(type, type);
case LogicalTypeId::TIME:
case LogicalTypeId::TIME_TZ:
return OP::template GetFunction<dtime_t, dtime_t>(type, type);
case LogicalTypeId::TIME_TZ:
// TIMETZ is ordered and interpolated by its normalized time, not by its raw bits
return OP::template GetFunction<dtime_tz_t, dtime_tz_t>(type, type);
case LogicalTypeId::INTERVAL:
return OP::template GetFunction<interval_t, interval_t>(type, type);
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ AggregateStateLayout QuantileStateLayout(AggregateLayoutInput &input) {
}
layout.total_state_size = AlignValue<idx_t>(sizeof(STATE));
layout.field = BuildStateField<STATE_FIELD>();
// NULL inputs are never buffered, so the finalize assumes all values are valid
layout.field.reject_null_elements = true;
AggregateStateField::PopulateListFunctions(layout.type, layout.field);
if (function.GetArguments().size() == 2) {
// the quantile parameter must be a constant at bind time (BindQuantile folds it into the bind data) -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ struct KahanSumState {
void Combine(const KahanSumState &other) {
this->is_set = other.is_set || this->is_set;
KahanAddInternal(other.value, this->value, this->err);
KahanAddInternal(other.err, this->value, this->err);
KahanAddInternal(-other.err, this->value, this->err);
}
};

Expand Down
33 changes: 14 additions & 19 deletions src/duckdb/extension/core_functions/scalar/enum/enum_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,25 @@ static void EnumRangeFunction(DataChunk &input, ExpressionState &state, Vector &
static void EnumRangeBoundaryFunction(DataChunk &input, ExpressionState &state, Vector &result) {
auto types = input.GetTypes();
D_ASSERT(types.size() == 2);
idx_t start, end;
auto first_param = input.GetValue(0, 0);
auto second_param = input.GetValue(1, 0);

// the binder guarantees that at least one of the parameters is an ENUM
auto &enum_type = types[0].id() == LogicalTypeId::ENUM ? types[0] : types[1];
auto &enum_vector = EnumType::GetValuesInsertOrder(enum_type);

if (first_param.IsNull()) {
start = 0;
} else {
start = first_param.GetValue<uint32_t>();
}
if (second_param.IsNull()) {
end = EnumType::GetSize(enum_type);
} else {
end = second_param.GetValue<uint32_t>() + 1;
auto enum_strings = enum_vector.Values<string_t>();

const auto count = input.size();
auto writer = FlatVector::Writer<VectorListType<string_t>>(result, count);
for (idx_t row = 0; row < count; row++) {
// a NULL boundary means that the range starts at the first / ends at the last value of the enum
auto first_param = input.GetValue(0, row);
auto second_param = input.GetValue(1, row);
idx_t start = first_param.IsNull() ? 0 : first_param.GetValue<uint32_t>();
idx_t end = second_param.IsNull() ? EnumType::GetSize(enum_type) : second_param.GetValue<uint32_t>() + 1;
idx_t enum_idx = start;
for (auto &child_writer : writer.WriteList(end > start ? end - start : 0)) {
child_writer.WriteValue(enum_strings[enum_idx++].GetValue());
}
}
vector<Value> enum_values;
for (idx_t i = start; i < end; i++) {
enum_values.emplace_back(enum_vector.GetValue(i));
}
auto val = Value::LIST(LogicalType::VARCHAR, enum_values);
result.Reference(val, count_t(input.size()));
}

static void EnumCodeFunction(DataChunk &input, ExpressionState &state, Vector &result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ void ExecuteFlatSlice(Vector &result, Vector &list_vector, Vector &begin_vector,
auto begin = begin_is_empty ? 0 : UnifiedVectorFormat::GetData<INDEX_TYPE>(begin_data)[begin_idx];
auto end = end_is_empty ? OP::ValueLength(sliced) : UnifiedVectorFormat::GetData<INDEX_TYPE>(end_data)[end_idx];
auto step = step_vector ? UnifiedVectorFormat::GetData<INDEX_TYPE>(step_data)[step_idx] : 1;
if (step == NumericLimits<INDEX_TYPE>::Minimum()) {
// the minimum cannot be negated - any step that is at least as large as the list selects the same entry
step = -NumericLimits<INDEX_TYPE>::Maximum();
}

if (step < 0) {
swap(begin, end);
Expand Down
5 changes: 2 additions & 3 deletions src/duckdb/extension/core_functions/scalar/math/numeric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1308,11 +1308,10 @@ struct ExpOperator {
} // namespace

ScalarFunction ExpFun::GetFunction() {
auto func = NameXArgument(
// not annotated monotone: libm exp is not monotone on every platform (Apple libm inverts adjacent doubles)
return NameXArgument(
ScalarFunction({}, LogicalType::DOUBLE, ScalarFunction::UnaryFunction<double, double, ExpOperator>),
LogicalType::DOUBLE);
func.SetUnaryArgProperties(ArgProperties().NonDecreasing());
return func;
}

//===--------------------------------------------------------------------===//
Expand Down
2 changes: 1 addition & 1 deletion src/duckdb/extension/icu/datetime/coptic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ int64_t CopticEthiopicCalendar::HandleComputeMonthStart(int32_t eyear, int32_t m
return int64_t(GetEpochOffset()) // the day the first year starts on
+ 365 * year // the whole years
+ FloorDiv::Divide(year, int64_t(4)) // their leap days
+ 30 * month // the whole months, which are all thirty days long
+ 30 * int64_t(month) // the whole months, which are all thirty days long
- 1; // the day before the first of the month
}

Expand Down
4 changes: 2 additions & 2 deletions src/duckdb/extension/icu/datetime/islamic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ void IslamicCalendar::HandleComputeFields(int32_t julian_day) {
// guess how many whole months have passed, then correct the guess
auto month = int32_t(std::floor(double(days) / Astronomer::SYNODIC_MONTH));
const auto start_date = int32_t(std::floor(month * Astronomer::SYNODIC_MONTH));
if (days - start_date >= 25 && MoonAge(GetTimeInternal()) > 0) {
if (days - start_date >= 25 && MoonAge(double(GetTimeInternal())) > 0) {
// the day is near the end of the month, so assume the next one and search backwards
month++;
}
Expand Down Expand Up @@ -291,7 +291,7 @@ int64_t IslamicUmalquraCalendar::YearStart(int32_t year) const {
year -= TABLE_START;
// a rounded least squares fit of the dates that follow from the observed month lengths,
// with a correction for the years it does not land on
const auto estimate = int64_t((354.36720 * double(year)) + 460322.05 + 0.5);
const auto estimate = int64_t(std::llround((354.36720 * double(year)) + 460322.05));
return estimate + UMALQURA_YEAR_START_FIX[year];
}

Expand Down
2 changes: 1 addition & 1 deletion src/duckdb/extension/icu/icu-datefunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ bool ICUDateFunc::TryGetTimeNS(Calendar *calendar, uint64_t nanos, timestamp_tz_
nanos %= Interval::NANOS_PER_MICRO;
timestamp_t us(tstz_micros);
timestamp_ns_t ns;
if (!Timestamp::TryFromTimestampNanos(us, nanos, ns)) {
if (!Timestamp::TryFromTimestampNanos(us, int32_t(nanos), ns)) {
return false;
}
result.value = ns.value;
Expand Down
4 changes: 4 additions & 0 deletions src/duckdb/extension/json/json_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ static inline idx_t ReadInteger(const char *ptr, const char *const end, idx_t &i
}
ptr++;
}
if (ptr == end || *ptr != ']') {
// No closing ']', or too many digits
return 0;
}
// Invalid if overflow
return idx >= (idx_t)IDX_T_MAX ? 0 : ptr - before;
}
Expand Down
2 changes: 2 additions & 0 deletions src/duckdb/extension/json/json_functions/json_merge_patch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ static inline yyjson_mut_val *MergePatch(yyjson_mut_doc *doc, yyjson_mut_val *or
if (nodes.orig) {
idx_t idx, max;
yyjson_mut_val *key, *orig_val;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro
yyjson_mut_obj_foreach(nodes.orig, idx, max, key, orig_val) {
auto patch_val =
yyjson_mut_obj_getn(nodes.patch, unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key));
Expand All @@ -49,6 +50,7 @@ static inline yyjson_mut_val *MergePatch(yyjson_mut_doc *doc, yyjson_mut_val *or
// Merge patch keys; null removes the key
idx_t idx, max;
yyjson_mut_val *key, *patch_val;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro
yyjson_mut_obj_foreach(nodes.patch, idx, max, key, patch_val) {
if (unsafe_yyjson_is_null(patch_val)) {
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ static unique_ptr<NodeStatistics> ReadSingleJSONFileCardinality(ClientContext &c
TableFunction JSONFunctions::GetReadSingleJSONFileTableFunction(shared_ptr<JSONScanInfo> function_info) {
const auto scan_type = function_info->type;
TableFunction table_function(
"read_single_json_file", FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR),
"read_single_json_file", FunctionSignature().AddPositionalOnly("file", LogicalType::ANY),
ReadSingleJSONFileFunction, ReadSingleJSONFileBind, ReadSingleJSONFileInitGlobal, ReadSingleJSONFileInitLocal);
JSONScan::TableFunctionDefaults(table_function);
if (scan_type != JSONScanType::READ_JSON_OBJECTS) {
Expand Down
2 changes: 2 additions & 0 deletions src/duckdb/extension/parquet/include/parquet_timestamp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ timestamp_t ParquetTimestampMsToTimestamp(const int64_t &raw_ts);
timestamp_ns_t ParquetTimestampNsToTimestampNs(const int64_t &raw_ns);

date_t ParquetIntToDate(const int32_t &raw_date);
//! Whether a raw TIME value is within a day, given the number of units in a day
bool ParquetTimeIsValid(const int64_t &raw, const int64_t day);
dtime_t ParquetMsIntToTime(const int32_t &raw_millis);
dtime_t ParquetIntToTime(const int64_t &raw_micros);

Expand Down
3 changes: 3 additions & 0 deletions src/duckdb/extension/parquet/parquet_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,9 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P
if (!s_ele.__isset.precision || !s_ele.__isset.scale) {
throw IOException("DECIMAL requires a length and scale specifier!");
}
if (s_ele.precision < 1 || s_ele.scale < 0 || s_ele.scale > s_ele.precision) {
throw IOException("Invalid DECIMAL precision %d and scale %d", s_ele.precision, s_ele.scale);
}
schema.type_scale = NumericCast<uint32_t>(s_ele.scale);
if (s_ele.precision > DecimalType::MaxWidth()) {
schema.type_info = ParquetExtraTypeInfo::DECIMAL_BYTE_ARRAY;
Expand Down
Loading
Loading