diff --git a/src/duckdb/extension/core_functions/function_list.cpp b/src/duckdb/extension/core_functions/function_list.cpp index 9e459831..53d96feb 100644 --- a/src/duckdb/extension/core_functions/function_list.cpp +++ b/src/duckdb/extension/core_functions/function_list.cpp @@ -261,6 +261,7 @@ static const StaticFunctionDefinition core_functions[] = { DUCKDB_SCALAR_FUNCTION_SET(MakeDateFun), DUCKDB_SCALAR_FUNCTION(MakeTimeFun), DUCKDB_SCALAR_FUNCTION_SET(MakeTimestampFun), + DUCKDB_SCALAR_FUNCTION_SET(MakeTimestampNsFun), DUCKDB_SCALAR_FUNCTION(MapFun), DUCKDB_SCALAR_FUNCTION(MapConcatFun), DUCKDB_SCALAR_FUNCTION(MapEntriesFun), diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp index 35b16f70..7256502a 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp @@ -285,6 +285,15 @@ struct MakeTimestampFun { static ScalarFunctionSet GetFunctions(); }; +struct MakeTimestampNsFun { + static constexpr const char *Name = "make_timestamp_ns"; + static constexpr const char *Parameters = "nanos"; + static constexpr const char *Description = "The timestamp for the given nanoseconds since epoch"; + static constexpr const char *Example = "make_timestamp(1732117793000000000)"; + + static ScalarFunctionSet GetFunctions(); +}; + struct MicrosecondsFun { static constexpr const char *Name = "microsecond"; static constexpr const char *Parameters = "ts"; diff --git a/src/duckdb/extension/core_functions/scalar/date/date_part.cpp b/src/duckdb/extension/core_functions/scalar/date/date_part.cpp index ce104b58..79d09226 100644 --- a/src/duckdb/extension/core_functions/scalar/date/date_part.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/date_part.cpp @@ -2065,6 +2065,19 @@ ScalarFunctionSet EpochFun::GetFunctions() { return GetTimePartFunction(LogicalType::DOUBLE); } +struct GetEpochNanosOperator { + static int64_t Operation(timestamp_ns_t timestamp) { + return Timestamp::GetEpochNanoSeconds(timestamp); + } +}; + +static void ExecuteGetNanosFromTimestampNs(DataChunk &input, ExpressionState &state, Vector &result) { + D_ASSERT(input.ColumnCount() == 1); + + auto func = GetEpochNanosOperator::Operation; + UnaryExecutor::Execute(input.data[0], result, input.size(), func); +} + ScalarFunctionSet EpochNsFun::GetFunctions() { using OP = DatePart::EpochNanosecondsOperator; auto operator_set = GetTimePartFunction(); @@ -2074,6 +2087,9 @@ ScalarFunctionSet EpochNsFun::GetFunctions() { auto tstz_stats = OP::template PropagateStatistics; operator_set.AddFunction( ScalarFunction({LogicalType::TIMESTAMP_TZ}, LogicalType::BIGINT, tstz_func, nullptr, nullptr, tstz_stats)); + + operator_set.AddFunction( + ScalarFunction({LogicalType::TIMESTAMP_NS}, LogicalType::BIGINT, ExecuteGetNanosFromTimestampNs)); return operator_set; } diff --git a/src/duckdb/extension/core_functions/scalar/date/make_date.cpp b/src/duckdb/extension/core_functions/scalar/date/make_date.cpp index 9af21110..3a526a1e 100644 --- a/src/duckdb/extension/core_functions/scalar/date/make_date.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/make_date.cpp @@ -102,8 +102,8 @@ struct MakeTimestampOperator { } template - static RESULT_TYPE Operation(T micros) { - return timestamp_t(micros); + static RESULT_TYPE Operation(T value) { + return RESULT_TYPE(value); } }; @@ -121,6 +121,15 @@ static void ExecuteMakeTimestamp(DataChunk &input, ExpressionState &state, Vecto SenaryExecutor::Execute(input, result, func); } +template +static void ExecuteMakeTimestampNs(DataChunk &input, ExpressionState &state, Vector &result) { + D_ASSERT(input.ColumnCount() == 1); + + auto func = MakeTimestampOperator::Operation; + UnaryExecutor::Execute(input.data[0], result, input.size(), func); + return; +} + ScalarFunctionSet MakeDateFun::GetFunctions() { ScalarFunctionSet make_date("make_date"); make_date.AddFunction(ScalarFunction({LogicalType::INTEGER}, LogicalType::DATE, MakeDateFromEpoch)); @@ -149,4 +158,11 @@ ScalarFunctionSet MakeTimestampFun::GetFunctions() { return operator_set; } +ScalarFunctionSet MakeTimestampNsFun::GetFunctions() { + ScalarFunctionSet operator_set("make_timestamp_ns"); + operator_set.AddFunction( + ScalarFunction({LogicalType::BIGINT}, LogicalType::TIMESTAMP_NS, ExecuteMakeTimestampNs)); + return operator_set; +} + } // namespace duckdb diff --git a/src/duckdb/src/common/assert.cpp b/src/duckdb/src/common/assert.cpp index 3397fce2..02f62212 100644 --- a/src/duckdb/src/common/assert.cpp +++ b/src/duckdb/src/common/assert.cpp @@ -10,8 +10,7 @@ void DuckDBAssertInternal(bool condition, const char *condition_name, const char if (condition) { return; } - throw InternalException("Assertion triggered in file \"%s\" on line %d: %s%s", file, linenr, condition_name, - Exception::GetStackTrace()); + throw InternalException("Assertion triggered in file \"%s\" on line %d: %s", file, linenr, condition_name); } } // namespace duckdb diff --git a/src/duckdb/src/common/error_data.cpp b/src/duckdb/src/common/error_data.cpp index 08163947..59557efc 100644 --- a/src/duckdb/src/common/error_data.cpp +++ b/src/duckdb/src/common/error_data.cpp @@ -4,6 +4,7 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/common/to_string.hpp" #include "duckdb/common/types.hpp" +#include "duckdb/common/stacktrace.hpp" #include "duckdb/parser/parsed_expression.hpp" #include "duckdb/parser/query_error_context.hpp" #include "duckdb/parser/tableref.hpp" @@ -102,12 +103,28 @@ void ErrorData::ConvertErrorToJSON() { final_message = raw_message; } +void ErrorData::FinalizeError() { + auto entry = extra_info.find("stack_trace_pointers"); + if (entry != extra_info.end()) { + auto stack_trace = StackTrace::ResolveStacktraceSymbols(entry->second); + extra_info["stack_trace"] = std::move(stack_trace); + extra_info.erase("stack_trace_pointers"); + } +} + void ErrorData::AddErrorLocation(const string &query) { - auto entry = extra_info.find("position"); - if (entry == extra_info.end()) { - return; + if (!query.empty()) { + auto entry = extra_info.find("position"); + if (entry != extra_info.end()) { + raw_message = QueryErrorContext::Format(query, raw_message, std::stoull(entry->second)); + } + } + { + auto entry = extra_info.find("stack_trace"); + if (entry != extra_info.end()) { + raw_message += "\n\nStack Trace:\n" + entry->second; + } } - raw_message = QueryErrorContext::Format(query, raw_message, std::stoull(entry->second)); final_message = ConstructFinalMessage(); } diff --git a/src/duckdb/src/common/exception.cpp b/src/duckdb/src/common/exception.cpp index 6f93e169..18722d6d 100644 --- a/src/duckdb/src/common/exception.cpp +++ b/src/duckdb/src/common/exception.cpp @@ -11,9 +11,7 @@ #include #include #endif -#ifdef DUCKDB_DEBUG_STACKTRACE -#include -#endif +#include "duckdb/common/stacktrace.hpp" namespace duckdb { @@ -32,13 +30,16 @@ string Exception::ToJSON(ExceptionType type, const string &message) { } string Exception::ToJSON(ExceptionType type, const string &message, const unordered_map &extra_info) { -#ifdef DUCKDB_DEBUG_STACKTRACE - auto extended_extra_info = extra_info; - extended_extra_info["stack_trace"] = Exception::GetStackTrace(); - return StringUtil::ExceptionToJSONMap(type, message, extended_extra_info); -#else - return StringUtil::ExceptionToJSONMap(type, message, extra_info); +#ifndef DUCKDB_DEBUG_STACKTRACE + // by default we only enable stack traces for internal exceptions + if (type == ExceptionType::INTERNAL) #endif + { + auto extended_extra_info = extra_info; + extended_extra_info["stack_trace_pointers"] = StackTrace::GetStacktracePointers(); + return StringUtil::ExceptionToJSONMap(type, message, extended_extra_info); + } + return StringUtil::ExceptionToJSONMap(type, message, extra_info); } bool Exception::UncaughtException() { @@ -73,22 +74,8 @@ bool Exception::InvalidatesDatabase(ExceptionType exception_type) { } } -string Exception::GetStackTrace(int max_depth) { -#ifdef DUCKDB_DEBUG_STACKTRACE - string result; - auto callstack = unique_ptr(new void *[max_depth]); - int frames = backtrace(callstack.get(), max_depth); - char **strs = backtrace_symbols(callstack.get(), frames); - for (int i = 0; i < frames; i++) { - result += strs[i]; - result += "\n"; - } - free(strs); - return "\n" + result; -#else - // Stack trace not available. Toggle DUCKDB_DEBUG_STACKTRACE in exception.cpp to enable stack traces. - return ""; -#endif +string Exception::GetStackTrace(idx_t max_depth) { + return StackTrace::GetStackTrace(max_depth); } string Exception::ConstructMessageRecursive(const string &msg, std::vector &values) { @@ -333,6 +320,7 @@ FatalException::FatalException(ExceptionType type, const string &msg) : Exceptio InternalException::InternalException(const string &msg) : Exception(ExceptionType::INTERNAL, msg) { #ifdef DUCKDB_CRASH_ON_ASSERT Printer::Print("ABORT THROWN BY INTERNAL EXCEPTION: " + msg); + Printer::Print(StackTrace::GetStackTrace()); abort(); #endif } diff --git a/src/duckdb/src/common/stacktrace.cpp b/src/duckdb/src/common/stacktrace.cpp new file mode 100644 index 00000000..ab73be9f --- /dev/null +++ b/src/duckdb/src/common/stacktrace.cpp @@ -0,0 +1,88 @@ +#include "duckdb/common/stacktrace.hpp" +#include "duckdb/common/string_util.hpp" +#include "duckdb/common/to_string.hpp" + +#if defined(__GLIBC__) || defined(__APPLE__) +#include +#include +#endif + +namespace duckdb { + +#if defined(__GLIBC__) || defined(__APPLE__) +static string UnmangleSymbol(string symbol) { + // find the mangled name + idx_t mangle_start = symbol.size(); + idx_t mangle_end = 0; + for (idx_t i = 0; i < symbol.size(); ++i) { + if (symbol[i] == '_') { + mangle_start = i; + break; + } + } + for (idx_t i = mangle_start; i < symbol.size(); i++) { + if (StringUtil::CharacterIsSpace(symbol[i])) { + mangle_end = i; + break; + } + } + if (mangle_start >= mangle_end) { + return symbol; + } + string mangled_symbol = symbol.substr(mangle_start, mangle_end - mangle_start); + + int status; + auto demangle_result = abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, nullptr, &status); + if (status != 0 || !demangle_result) { + return symbol; + } + string result; + result += symbol.substr(0, mangle_start); + result += demangle_result; + result += symbol.substr(mangle_end); + free(demangle_result); + return result; +} + +string StackTrace::GetStacktracePointers(idx_t max_depth) { + string result; + auto callstack = unique_ptr(new void *[max_depth]); + int frames = backtrace(callstack.get(), NumericCast(max_depth)); + // skip two frames (these are always StackTrace::...) + for (idx_t i = 2; i < NumericCast(frames); i++) { + if (!result.empty()) { + result += ";"; + } + result += to_string(CastPointerToValue(callstack[i])); + } + return result; +} + +string StackTrace::ResolveStacktraceSymbols(const string &pointers) { + auto splits = StringUtil::Split(pointers, ";"); + idx_t frame_count = splits.size(); + auto callstack = unique_ptr(new void *[frame_count]); + for (idx_t i = 0; i < frame_count; i++) { + callstack[i] = cast_uint64_to_pointer(StringUtil::ToUnsigned(splits[i])); + } + string result; + char **strs = backtrace_symbols(callstack.get(), NumericCast(frame_count)); + for (idx_t i = 0; i < frame_count; i++) { + result += UnmangleSymbol(strs[i]); + result += "\n"; + } + free(reinterpret_cast(strs)); + return "\n" + result; +} + +#else +string StackTrace::GetStacktracePointers(idx_t max_depth) { + return string(); +} + +string StackTrace::ResolveStacktraceSymbols(const string &pointers) { + return string(); +} +#endif + +} // namespace duckdb diff --git a/src/duckdb/src/common/string_util.cpp b/src/duckdb/src/common/string_util.cpp index 0493b626..f1bd1e03 100644 --- a/src/duckdb/src/common/string_util.cpp +++ b/src/duckdb/src/common/string_util.cpp @@ -53,6 +53,10 @@ bool StringUtil::Contains(const string &haystack, const char &needle_char) { return (haystack.find(needle_char) != string::npos); } +idx_t StringUtil::ToUnsigned(const string &str) { + return std::stoull(str); +} + void StringUtil::LTrim(string &str) { auto it = str.begin(); while (it != str.end() && CharacterIsSpace(*it)) { diff --git a/src/duckdb/src/common/types/timestamp.cpp b/src/duckdb/src/common/types/timestamp.cpp index f23ee214..dbc40a6e 100644 --- a/src/duckdb/src/common/types/timestamp.cpp +++ b/src/duckdb/src/common/types/timestamp.cpp @@ -457,6 +457,11 @@ int64_t Timestamp::GetEpochNanoSeconds(timestamp_t timestamp) { return result; } +int64_t Timestamp::GetEpochNanoSeconds(timestamp_ns_t timestamp) { + D_ASSERT(Timestamp::IsFinite(timestamp)); + return timestamp.value; +} + int64_t Timestamp::GetEpochRounded(timestamp_t input, int64_t power_of_ten) { D_ASSERT(Timestamp::IsFinite(input)); // Round away from the epoch. diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index bdcf6722..361cf20b 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "4-dev3588" +#define DUCKDB_PATCH_VERSION "4-dev3633" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 1 @@ -8,10 +8,10 @@ #define DUCKDB_MAJOR_VERSION 1 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v1.1.4-dev3588" +#define DUCKDB_VERSION "v1.1.4-dev3633" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "a91feadf8c" +#define DUCKDB_SOURCE_ID "78ebe44ef9" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/include/duckdb.h b/src/duckdb/src/include/duckdb.h index c62b5fbe..e2fdbfc5 100644 --- a/src/duckdb/src/include/duckdb.h +++ b/src/duckdb/src/include/duckdb.h @@ -3892,6 +3892,20 @@ Append a DEFAULT value (NULL if DEFAULT not available for column) to the appende */ DUCKDB_API duckdb_state duckdb_append_default(duckdb_appender appender); +/*! +Append a DEFAULT value, at the specified row and column, (NULL if DEFAULT not available for column) to the chunk created +from the specified appender. The default value of the column must be a constant value. Non-deterministic expressions +like nextval('seq') or random() are not supported. + +* @param appender The appender to get the default value from. +* @param chunk The data chunk to append the default value to. +* @param col The chunk column index to append the default value to. +* @param row The chunk row index to append the default value to. +* @return `DuckDBSuccess` on success or `DuckDBError` on failure. +*/ +DUCKDB_API duckdb_state duckdb_append_default_to_chunk(duckdb_appender appender, duckdb_data_chunk chunk, idx_t col, + idx_t row); + /*! Append a bool value to the appender. */ diff --git a/src/duckdb/src/include/duckdb/common/error_data.hpp b/src/duckdb/src/include/duckdb/common/error_data.hpp index 93ff5ccb..20b7541f 100644 --- a/src/duckdb/src/include/duckdb/common/error_data.hpp +++ b/src/duckdb/src/include/duckdb/common/error_data.hpp @@ -47,6 +47,7 @@ class ErrorData { return extra_info; } + DUCKDB_API void FinalizeError(); DUCKDB_API void AddErrorLocation(const string &query); DUCKDB_API void ConvertErrorToJSON(); diff --git a/src/duckdb/src/include/duckdb/common/exception.hpp b/src/duckdb/src/include/duckdb/common/exception.hpp index 3bb60592..71c4ff8b 100644 --- a/src/duckdb/src/include/duckdb/common/exception.hpp +++ b/src/duckdb/src/include/duckdb/common/exception.hpp @@ -137,7 +137,7 @@ class Exception : public std::runtime_error { DUCKDB_API static bool UncaughtException(); - DUCKDB_API static string GetStackTrace(int max_depth = 120); + DUCKDB_API static string GetStackTrace(idx_t max_depth = 120); static string FormatStackTrace(const string &message = "") { return (message + "\n" + GetStackTrace()); } diff --git a/src/duckdb/src/include/duckdb/common/stacktrace.hpp b/src/duckdb/src/include/duckdb/common/stacktrace.hpp new file mode 100644 index 00000000..eb3f8e69 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/stacktrace.hpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/stacktrace.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/common.hpp" + +namespace duckdb { + +class StackTrace { +public: + static string GetStacktracePointers(idx_t max_depth = 120); + static string ResolveStacktraceSymbols(const string &pointers); + + inline static string GetStackTrace(idx_t max_depth = 120) { + return ResolveStacktraceSymbols(GetStacktracePointers(max_depth)); + } +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/string_util.hpp b/src/duckdb/src/include/duckdb/common/string_util.hpp index 49e4128b..7ccc8042 100644 --- a/src/duckdb/src/include/duckdb/common/string_util.hpp +++ b/src/duckdb/src/include/duckdb/common/string_util.hpp @@ -165,6 +165,8 @@ class StringUtil { DUCKDB_API static void URLDecodeBuffer(const char *input, idx_t input_size, char *output, bool plus_to_space = false); + DUCKDB_API static idx_t ToUnsigned(const string &str); + template static string ToString(const vector &input, const string &separator) { vector input_list; diff --git a/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp b/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp index 3369f5e0..cb6ebcba 100644 --- a/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp +++ b/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp @@ -63,6 +63,9 @@ class DataChunk { inline void SetCardinality(const DataChunk &other) { SetCardinality(other.size()); } + inline idx_t GetCapacity() const { + return capacity; + } inline void SetCapacity(idx_t capacity_p) { this->capacity = capacity_p; } diff --git a/src/duckdb/src/include/duckdb/common/types/timestamp.hpp b/src/duckdb/src/include/duckdb/common/types/timestamp.hpp index 45b28cf4..eaab803e 100644 --- a/src/duckdb/src/include/duckdb/common/types/timestamp.hpp +++ b/src/duckdb/src/include/duckdb/common/types/timestamp.hpp @@ -24,6 +24,8 @@ struct dtime_tz_t; // NOLINT //! Type used to represent a TIMESTAMP. timestamp_t holds the microseconds since 1970-01-01. struct timestamp_t { // NOLINT + // NOTE: The unit of value is microseconds for timestamp_t, but it can be + // different for subclasses (e.g. it's nanos for timestamp_ns, etc). int64_t value; timestamp_t() = default; @@ -189,6 +191,7 @@ class Timestamp { DUCKDB_API static int64_t GetEpochMicroSeconds(timestamp_t timestamp); //! Convert a timestamp to epoch (in nanoseconds) DUCKDB_API static int64_t GetEpochNanoSeconds(timestamp_t timestamp); + DUCKDB_API static int64_t GetEpochNanoSeconds(timestamp_ns_t timestamp); //! Convert a timestamp to a rounded epoch at a given resolution. DUCKDB_API static int64_t GetEpochRounded(timestamp_t timestamp, const int64_t power_of_ten); //! Convert a timestamp to a Julian Day diff --git a/src/duckdb/src/include/duckdb/main/appender.hpp b/src/duckdb/src/include/duckdb/main/appender.hpp index 70dc881e..0f0f0a4f 100644 --- a/src/duckdb/src/include/duckdb/main/appender.hpp +++ b/src/duckdb/src/include/duckdb/main/appender.hpp @@ -67,6 +67,7 @@ class BaseAppender { // Append functions template void Append(T value) = delete; + DUCKDB_API void Append(DataChunk &target, const Value &value, idx_t col, idx_t row); DUCKDB_API void Append(const char *value, uint32_t length); @@ -120,6 +121,7 @@ class BaseAppender { } void AppendValue(const Value &value); + void AppendValue(DataChunk target, const Value &value); }; class Appender : public BaseAppender { @@ -143,11 +145,13 @@ class Appender : public BaseAppender { public: void AppendDefault(); + void AppendDefault(DataChunk &chunk, idx_t col, idx_t row); void AddColumn(const string &name) override; void ClearColumns() override; protected: void FlushInternal(ColumnDataCollection &collection) override; + Value GetDefaultValue(idx_t column); }; class InternalAppender : public BaseAppender { diff --git a/src/duckdb/src/include/duckdb/main/capi/extension_api.hpp b/src/duckdb/src/include/duckdb/main/capi/extension_api.hpp index c8541ef1..6a8bb4bc 100644 --- a/src/duckdb/src/include/duckdb/main/capi/extension_api.hpp +++ b/src/duckdb/src/include/duckdb/main/capi/extension_api.hpp @@ -460,6 +460,10 @@ typedef struct { duckdb_arrow_schema arrow_schema, duckdb_arrow_array arrow_array, duckdb_arrow_stream *out_stream); duckdb_data_chunk (*duckdb_stream_fetch_chunk)(duckdb_result result); + // New append functions that are added + + duckdb_state (*duckdb_append_default_to_chunk)(duckdb_appender appender, duckdb_data_chunk chunk, idx_t col, + idx_t row); } duckdb_ext_api_v1; //===--------------------------------------------------------------------===// @@ -871,6 +875,7 @@ inline duckdb_ext_api_v1 CreateAPIv1() { result.duckdb_arrow_scan = duckdb_arrow_scan; result.duckdb_arrow_array_scan = duckdb_arrow_array_scan; result.duckdb_stream_fetch_chunk = duckdb_stream_fetch_chunk; + result.duckdb_append_default_to_chunk = duckdb_append_default_to_chunk; return result; } diff --git a/src/duckdb/src/include/duckdb/main/extension_entries.hpp b/src/duckdb/src/include/duckdb/main/extension_entries.hpp index c1cb16d6..b0d0c0c4 100644 --- a/src/duckdb/src/include/duckdb/main/extension_entries.hpp +++ b/src/duckdb/src/include/duckdb/main/extension_entries.hpp @@ -424,6 +424,7 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"make_date", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"make_time", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"make_timestamp", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"make_timestamp_ns", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"make_timestamptz", "icu", CatalogType::SCALAR_FUNCTION_ENTRY}, {"map", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"map_concat", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, diff --git a/src/duckdb/src/include/duckdb/main/relation.hpp b/src/duckdb/src/include/duckdb/main/relation.hpp index e5fbafaf..37b137aa 100644 --- a/src/duckdb/src/include/duckdb/main/relation.hpp +++ b/src/duckdb/src/include/duckdb/main/relation.hpp @@ -168,9 +168,12 @@ class Relation : public enable_shared_from_this { DUCKDB_API void Insert(vector>> &&expressions); //! Create a table and insert the data from this relation into that table DUCKDB_API shared_ptr CreateRel(const string &schema_name, const string &table_name, - bool temporary = false); - DUCKDB_API void Create(const string &table_name, bool temporary = false); - DUCKDB_API void Create(const string &schema_name, const string &table_name, bool temporary = false); + bool temporary = false, + OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT); + DUCKDB_API void Create(const string &table_name, bool temporary = false, + OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT); + DUCKDB_API void Create(const string &schema_name, const string &table_name, bool temporary = false, + OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT); //! Write a relation to a CSV file DUCKDB_API shared_ptr diff --git a/src/duckdb/src/include/duckdb/main/relation/create_table_relation.hpp b/src/duckdb/src/include/duckdb/main/relation/create_table_relation.hpp index 54686dd3..7d546294 100644 --- a/src/duckdb/src/include/duckdb/main/relation/create_table_relation.hpp +++ b/src/duckdb/src/include/duckdb/main/relation/create_table_relation.hpp @@ -14,13 +14,15 @@ namespace duckdb { class CreateTableRelation : public Relation { public: - CreateTableRelation(shared_ptr child, string schema_name, string table_name, bool temporary); + CreateTableRelation(shared_ptr child, string schema_name, string table_name, bool temporary, + OnCreateConflict on_conflict); shared_ptr child; string schema_name; string table_name; vector columns; bool temporary; + OnCreateConflict on_conflict; public: BoundStatement Bind(Binder &binder) override; diff --git a/src/duckdb/src/include/duckdb_extension.h b/src/duckdb/src/include/duckdb_extension.h index f746830f..1c57e3f9 100644 --- a/src/duckdb/src/include/duckdb_extension.h +++ b/src/duckdb/src/include/duckdb_extension.h @@ -528,6 +528,12 @@ typedef struct { duckdb_data_chunk (*duckdb_stream_fetch_chunk)(duckdb_result result); #endif +// New append functions that are added +#ifdef DUCKDB_EXTENSION_API_VERSION_UNSTABLE + duckdb_state (*duckdb_append_default_to_chunk)(duckdb_appender appender, duckdb_data_chunk chunk, idx_t col, + idx_t row); +#endif + } duckdb_ext_api_v1; //===--------------------------------------------------------------------===// @@ -942,6 +948,9 @@ typedef struct { #define duckdb_arrow_array_scan duckdb_ext_api.duckdb_arrow_array_scan #define duckdb_stream_fetch_chunk duckdb_ext_api.duckdb_stream_fetch_chunk +// Version unstable_new_append_functions +#define duckdb_append_default_to_chunk duckdb_ext_api.duckdb_append_default_to_chunk + //===--------------------------------------------------------------------===// // Struct Global Macros //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/main/appender.cpp b/src/duckdb/src/main/appender.cpp index dde56b65..11a5753a 100644 --- a/src/duckdb/src/main/appender.cpp +++ b/src/duckdb/src/main/appender.cpp @@ -373,6 +373,28 @@ void BaseAppender::Append(Value value) { // NOLINT: template stuff AppendValue(value); } +void duckdb::BaseAppender::Append(DataChunk &target, const Value &value, idx_t col, idx_t row) { + if (col >= target.ColumnCount()) { + throw InvalidInputException("Too many appends for chunk!"); + } + if (row >= target.GetCapacity()) { + throw InvalidInputException("Too many rows for chunk!"); + } + + if (value.type() == target.GetTypes()[col]) { + target.SetValue(col, row, value); + } else { + Value new_value; + string error_msg; + if (value.DefaultTryCastAs(target.GetTypes()[col], new_value, &error_msg)) { + target.SetValue(col, row, new_value); + } else { + throw InvalidInputException("type mismatch in Append, expected %s, got %s for column %d", + target.GetTypes()[col], value.type(), col); + } + } +} + template <> void BaseAppender::Append(std::nullptr_t value) { if (column >= chunk.ColumnCount()) { @@ -464,15 +486,32 @@ void Appender::FlushInternal(ColumnDataCollection &collection) { } void Appender::AppendDefault() { - auto index = column_ids.empty() ? column : column_ids[column].index; + auto value = GetDefaultValue(column); + Append(value); +} + +void duckdb::Appender::AppendDefault(DataChunk &chunk, idx_t col, idx_t row) { + auto value = GetDefaultValue(col); + Append(chunk, value, col, row); +} + +Value Appender::GetDefaultValue(idx_t column) { + auto index = column; + + if (!column_ids.empty()) { + if (column >= column_ids.size()) { + throw InvalidInputException("Column index out of bounds"); + } + index = column_ids[column].index; + } + auto it = default_values.find(index); if (it == default_values.end()) { auto &name = description->columns[index].Name(); throw NotImplementedException( "AppendDefault is not supported for column \"%s\": not a foldable default expressions.", name); } - auto &value = it->second; - Append(value); + return it->second; } void Appender::AddColumn(const string &name) { diff --git a/src/duckdb/src/main/capi/appender-c.cpp b/src/duckdb/src/main/capi/appender-c.cpp index 288d58ed..cab3291f 100644 --- a/src/duckdb/src/main/capi/appender-c.cpp +++ b/src/duckdb/src/main/capi/appender-c.cpp @@ -145,6 +145,27 @@ duckdb_state duckdb_append_default(duckdb_appender appender) { return DuckDBSuccess; } +duckdb_state duckdb_append_default_to_chunk(duckdb_appender appender, duckdb_data_chunk chunk, idx_t col, idx_t row) { + if (!appender || !chunk) { + return DuckDBError; + } + + auto *appender_instance = reinterpret_cast(appender); + + auto data_chunk = reinterpret_cast(chunk); + + try { + appender_instance->appender->AppendDefault(*data_chunk, col, row); + } catch (std::exception &ex) { + ErrorData error(ex); + appender_instance->error = error.RawMessage(); + return DuckDBError; + } catch (...) { + return DuckDBError; + } + return DuckDBSuccess; +} + duckdb_state duckdb_append_bool(duckdb_appender appender, bool value) { return duckdb_append_internal(appender, value); } diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index fddfea8a..94e41371 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -170,9 +170,10 @@ void ClientContext::Destroy() { } void ClientContext::ProcessError(ErrorData &error, const string &query) const { + error.FinalizeError(); if (config.errors_as_json) { error.ConvertErrorToJSON(); - } else if (!query.empty()) { + } else { error.AddErrorLocation(query); } } diff --git a/src/duckdb/src/main/relation.cpp b/src/duckdb/src/main/relation.cpp index 8ed87778..e43e55c5 100644 --- a/src/duckdb/src/main/relation.cpp +++ b/src/duckdb/src/main/relation.cpp @@ -270,16 +270,18 @@ void Relation::Insert(vector>> &&expressions rel->Insert(GetAlias()); } -shared_ptr Relation::CreateRel(const string &schema_name, const string &table_name, bool temporary) { - return make_shared_ptr(shared_from_this(), schema_name, table_name, temporary); +shared_ptr Relation::CreateRel(const string &schema_name, const string &table_name, bool temporary, + OnCreateConflict on_conflict) { + return make_shared_ptr(shared_from_this(), schema_name, table_name, temporary, on_conflict); } -void Relation::Create(const string &table_name, bool temporary) { - Create(INVALID_SCHEMA, table_name, temporary); +void Relation::Create(const string &table_name, bool temporary, OnCreateConflict on_conflict) { + Create(INVALID_SCHEMA, table_name, temporary, on_conflict); } -void Relation::Create(const string &schema_name, const string &table_name, bool temporary) { - auto create = CreateRel(schema_name, table_name, temporary); +void Relation::Create(const string &schema_name, const string &table_name, bool temporary, + OnCreateConflict on_conflict) { + auto create = CreateRel(schema_name, table_name, temporary, on_conflict); auto res = create->Execute(); if (res->HasError()) { const string prepended_message = "Failed to create table '" + table_name + "': "; diff --git a/src/duckdb/src/main/relation/create_table_relation.cpp b/src/duckdb/src/main/relation/create_table_relation.cpp index 7ff390e0..2492f244 100644 --- a/src/duckdb/src/main/relation/create_table_relation.cpp +++ b/src/duckdb/src/main/relation/create_table_relation.cpp @@ -7,9 +7,10 @@ namespace duckdb { CreateTableRelation::CreateTableRelation(shared_ptr child_p, string schema_name, string table_name, - bool temporary_p) + bool temporary_p, OnCreateConflict on_conflict) : Relation(child_p->context, RelationType::CREATE_TABLE_RELATION), child(std::move(child_p)), - schema_name(std::move(schema_name)), table_name(std::move(table_name)), temporary(temporary_p) { + schema_name(std::move(schema_name)), table_name(std::move(table_name)), temporary(temporary_p), + on_conflict(on_conflict) { TryBindRelation(columns); } @@ -22,7 +23,7 @@ BoundStatement CreateTableRelation::Bind(Binder &binder) { info->schema = schema_name; info->table = table_name; info->query = std::move(select); - info->on_conflict = OnCreateConflict::ERROR_ON_CONFLICT; + info->on_conflict = on_conflict; info->temporary = temporary; stmt.info = std::move(info); return binder.Bind(stmt.Cast()); diff --git a/src/duckdb/src/storage/single_file_block_manager.cpp b/src/duckdb/src/storage/single_file_block_manager.cpp index 7f6231bf..00d8aa94 100644 --- a/src/duckdb/src/storage/single_file_block_manager.cpp +++ b/src/duckdb/src/storage/single_file_block_manager.cpp @@ -179,7 +179,7 @@ void SingleFileBlockManager::CreateNewDatabase() { MainHeader main_header; main_header.version_number = VERSION_NUMBER; - memset(main_header.flags, 0, sizeof(uint64_t) * 4); + memset(main_header.flags, 0, sizeof(uint64_t) * MainHeader::FLAG_COUNT); SerializeHeaderStructure(main_header, header_buffer.buffer); // now write the header to the file diff --git a/src/duckdb/ub_src_common.cpp b/src/duckdb/ub_src_common.cpp index 626816b8..2632a127 100644 --- a/src/duckdb/ub_src_common.cpp +++ b/src/duckdb/ub_src_common.cpp @@ -56,6 +56,8 @@ #include "src/common/random_engine.cpp" +#include "src/common/stacktrace.cpp" + #include "src/common/string_util.cpp" #include "src/common/enum_util.cpp"