diff --git a/src/duckdb/src/common/extra_type_info.cpp b/src/duckdb/src/common/extra_type_info.cpp index 54f03447c..a32129fd2 100644 --- a/src/duckdb/src/common/extra_type_info.cpp +++ b/src/duckdb/src/common/extra_type_info.cpp @@ -10,21 +10,12 @@ namespace duckdb { //===--------------------------------------------------------------------===// -// Extra Type Info +// Extension Type Info //===--------------------------------------------------------------------===// -ExtraTypeInfo::ExtraTypeInfo(ExtraTypeInfoType type) : type(type) { -} -ExtraTypeInfo::ExtraTypeInfo(ExtraTypeInfoType type, string alias) : type(type), alias(std::move(alias)) { -} -ExtraTypeInfo::~ExtraTypeInfo() { -} -shared_ptr ExtraTypeInfo::Copy() const { - return make_shared_ptr(*this); -} static bool CompareModifiers(const vector &left, const vector &right) { // Check if the common prefix of the properties is the same for both types - auto common_props = MinValue(left.size(), right.size()); + const auto common_props = MinValue(left.size(), right.size()); for (idx_t i = 0; i < common_props; i++) { if (left[i].type() != right[i].type()) { return false; @@ -34,6 +25,7 @@ static bool CompareModifiers(const vector &left, const vector &rig if (left[i].IsNull() || right[i].IsNull()) { continue; } + if (left[i] != right[i]) { return false; } @@ -41,6 +33,57 @@ static bool CompareModifiers(const vector &left, const vector &rig return true; } +bool ExtensionTypeInfo::Equals(optional_ptr other_p) const { + if (other_p == nullptr) { + return false; + } + if (!CompareModifiers(modifiers, other_p->modifiers)) { + return false; + } + + // Properties are optional, so only compare those present in both + for (auto &kv : properties) { + auto it = other_p->properties.find(kv.first); + if (it == other_p->properties.end()) { + continue; + } + if (kv.second != it->second) { + return false; + } + } + + return true; +} + +//===--------------------------------------------------------------------===// +// Extra Type Info +//===--------------------------------------------------------------------===// +ExtraTypeInfo::ExtraTypeInfo(ExtraTypeInfoType type) : type(type) { +} +ExtraTypeInfo::ExtraTypeInfo(ExtraTypeInfoType type, string alias) : type(type), alias(std::move(alias)) { +} +ExtraTypeInfo::~ExtraTypeInfo() { +} + +ExtraTypeInfo::ExtraTypeInfo(const ExtraTypeInfo &other) : type(other.type), alias(other.alias) { + if (other.extension_info) { + extension_info = make_uniq(*other.extension_info); + } +} + +ExtraTypeInfo &ExtraTypeInfo::operator=(const ExtraTypeInfo &other) { + type = other.type; + alias = other.alias; + if (other.extension_info) { + extension_info = make_uniq(*other.extension_info); + } + return *this; +} + +shared_ptr ExtraTypeInfo::Copy() const { + return shared_ptr(new ExtraTypeInfo(*this)); +} + bool ExtraTypeInfo::Equals(ExtraTypeInfo *other_p) const { if (type == ExtraTypeInfoType::INVALID_TYPE_INFO || type == ExtraTypeInfoType::STRING_TYPE_INFO || type == ExtraTypeInfoType::GENERIC_TYPE_INFO) { @@ -54,7 +97,7 @@ bool ExtraTypeInfo::Equals(ExtraTypeInfo *other_p) const { if (alias != other_p->alias) { return false; } - if (!CompareModifiers(modifiers, other_p->modifiers)) { + if (extension_info && !extension_info->Equals(other_p->extension_info.get())) { return false; } return true; @@ -68,7 +111,7 @@ bool ExtraTypeInfo::Equals(ExtraTypeInfo *other_p) const { if (alias != other_p->alias) { return false; } - if (!CompareModifiers(modifiers, other_p->modifiers)) { + if (extension_info && !extension_info->Equals(other_p->extension_info.get())) { return false; } return EqualsInternal(other_p); diff --git a/src/duckdb/src/common/types.cpp b/src/duckdb/src/common/types.cpp index 6c3a79130..52002ded4 100644 --- a/src/duckdb/src/common/types.cpp +++ b/src/duckdb/src/common/types.cpp @@ -365,21 +365,29 @@ bool TypeIsInteger(PhysicalType type) { type == PhysicalType::UINT128; } +static string TypeModifierListToString(const vector &mod_list) { + string result; + if (mod_list.empty()) { + return result; + } + result = "("; + for (idx_t i = 0; i < mod_list.size(); i++) { + result += mod_list[i].ToString(); + if (i < mod_list.size() - 1) { + result += ", "; + } + } + result += ")"; + return result; +} + string LogicalType::ToString() const { if (id_ != LogicalTypeId::USER) { auto alias = GetAlias(); if (!alias.empty()) { - auto mods_ptr = GetModifiers(); - if (mods_ptr && !mods_ptr->empty()) { - auto &mods = *mods_ptr; - alias += "("; - for (idx_t i = 0; i < mods.size(); i++) { - alias += mods[i].ToString(); - if (i < mods.size() - 1) { - alias += ", "; - } - } - alias += ")"; + if (HasExtensionInfo()) { + auto &ext_info = *GetExtensionInfo(); + alias += TypeModifierListToString(ext_info.modifiers); } return alias; } @@ -491,14 +499,7 @@ string LogicalType::ToString() const { result += KeywordHelper::WriteOptionallyQuoted(type); if (!mods.empty()) { - result += "("; - for (idx_t i = 0; i < mods.size(); i++) { - result += mods[i].ToString(); - if (i < mods.size() - 1) { - result += ", "; - } - } - result += ")"; + result += TypeModifierListToString(mods); } return result; @@ -1360,51 +1361,32 @@ bool LogicalType::HasAlias() const { return false; } -void LogicalType::SetModifiers(vector modifiers) { - if (!type_info_ && !modifiers.empty()) { - type_info_ = make_shared_ptr(ExtraTypeInfoType::GENERIC_TYPE_INFO); - } - type_info_->modifiers = std::move(modifiers); -} - -bool LogicalType::HasModifiers() const { - if (id() == LogicalTypeId::USER) { - return !UserType::GetTypeModifiers(*this).empty(); - } - if (type_info_) { - return !type_info_->modifiers.empty(); +bool LogicalType::HasExtensionInfo() const { + if (type_info_ && type_info_->extension_info) { + return true; } return false; } -vector LogicalType::GetModifiersCopy() const { - if (id() == LogicalTypeId::USER) { - return UserType::GetTypeModifiers(*this); - } - if (type_info_) { - return type_info_->modifiers; +optional_ptr LogicalType::GetExtensionInfo() const { + if (type_info_ && type_info_->extension_info) { + return type_info_->extension_info.get(); } - return {}; + return nullptr; } -optional_ptr> LogicalType::GetModifiers() { - if (id() == LogicalTypeId::USER) { - return UserType::GetTypeModifiers(*this); - } - if (type_info_) { - return type_info_->modifiers; +optional_ptr LogicalType::GetExtensionInfo() { + if (type_info_ && type_info_->extension_info) { + return type_info_->extension_info.get(); } return nullptr; } -optional_ptr> LogicalType::GetModifiers() const { - if (id() == LogicalTypeId::USER) { - return UserType::GetTypeModifiers(*this); - } - if (type_info_) { - return type_info_->modifiers; +void LogicalType::SetExtensionInfo(unique_ptr info) { + if (!type_info_) { + type_info_ = make_shared_ptr(ExtraTypeInfoType::GENERIC_TYPE_INFO); } - return nullptr; + type_info_->extension_info = std::move(info); } //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp b/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp index 353c091c8..6ad354ea2 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp @@ -42,6 +42,7 @@ SourceResultType PhysicalCopyDatabase::GetData(ExecutionContext &context, DataCh catalog.CreateType(context.client, create_info->Cast()); break; case CatalogType::MACRO_ENTRY: + case CatalogType::TABLE_MACRO_ENTRY: catalog.CreateFunction(context.client, create_info->Cast()); break; case CatalogType::TABLE_ENTRY: { diff --git a/src/duckdb/src/execution/physical_plan/plan_get.cpp b/src/duckdb/src/execution/physical_plan/plan_get.cpp index 4aba3a443..3b5d940eb 100644 --- a/src/duckdb/src/execution/physical_plan/plan_get.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_get.cpp @@ -160,8 +160,9 @@ unique_ptr PhysicalPlanGenerator::CreatePlan(LogicalGet &op) { vector> expressions; for (auto &column_id : column_ids) { if (column_id.IsRowIdColumn()) { - types.emplace_back(LogicalType::ROW_TYPE); - expressions.push_back(make_uniq(Value::BIGINT(0))); + types.emplace_back(op.GetRowIdType()); + // Now how to make that a constant expression. + expressions.push_back(make_uniq(Value(op.GetRowIdType()))); } else { auto col_id = column_id.GetPrimaryIndex(); auto type = op.returned_types[col_id]; diff --git a/src/duckdb/src/function/table/arrow_conversion.cpp b/src/duckdb/src/function/table/arrow_conversion.cpp index 261635e1c..18daff2b9 100644 --- a/src/duckdb/src/function/table/arrow_conversion.cpp +++ b/src/duckdb/src/function/table/arrow_conversion.cpp @@ -1340,7 +1340,8 @@ static void ColumnArrowToDuckDBDictionary(Vector &vector, ArrowArray &array, Arr } void ArrowTableFunction::ArrowToDuckDB(ArrowScanLocalState &scan_state, const arrow_column_map_t &arrow_convert_data, - DataChunk &output, idx_t start, bool arrow_scan_is_projected) { + DataChunk &output, idx_t start, bool arrow_scan_is_projected, + idx_t rowid_column_index) { for (idx_t idx = 0; idx < output.ColumnCount(); idx++) { auto col_idx = scan_state.column_ids.empty() ? idx : scan_state.column_ids[idx]; @@ -1348,9 +1349,22 @@ void ArrowTableFunction::ArrowToDuckDB(ArrowScanLocalState &scan_state, const ar // table function, we need to use original column ids here. auto arrow_array_idx = arrow_scan_is_projected ? idx : col_idx; - if (col_idx == COLUMN_IDENTIFIER_ROW_ID) { - // This column is skipped by the projection pushdown - continue; + if (rowid_column_index != COLUMN_IDENTIFIER_ROW_ID) { + if (col_idx == COLUMN_IDENTIFIER_ROW_ID) { + arrow_array_idx = rowid_column_index; + } else if (col_idx >= rowid_column_index) { + // Since the rowid column is skipped when the table is bound (its not a named column), + // we need to shift references forward in the Arrow array by one to match the alignment + // that DuckDB believes the Arrow array is in. + col_idx += 1; + arrow_array_idx += 1; + } + } else { + // If there isn't any defined row_id_index, and we're asked for it, skip the column. + // This is the incumbent behavior. + if (col_idx == COLUMN_IDENTIFIER_ROW_ID) { + continue; + } } auto &parent_array = scan_state.chunk->arrow_array; diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index c68f78fca..167f071af 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-dev3961" +#define DUCKDB_PATCH_VERSION "4-dev3985" #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-dev3961" +#define DUCKDB_VERSION "v1.1.4-dev3985" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "4488c61ee7" +#define DUCKDB_SOURCE_ID "adc6f607a7" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp index 5ce45af7f..398e49974 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp @@ -117,6 +117,11 @@ class TableCatalogEntry : public StandardEntry { //! Returns true, if the table has a primary key, else false. bool HasPrimaryKey() const; + //! Returns the rowid type of this table + virtual LogicalType GetRowIdType() const { + return LogicalType::ROW_TYPE; + } + protected: //! A list of columns that are part of this table ColumnList columns; diff --git a/src/duckdb/src/include/duckdb/common/extension_type_info.hpp b/src/duckdb/src/include/duckdb/common/extension_type_info.hpp new file mode 100644 index 000000000..48e12d3c3 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/extension_type_info.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "duckdb/common/string.hpp" +#include "duckdb/common/types/value.hpp" +#include "duckdb/common/serializer/serializer.hpp" + +namespace duckdb { + +struct ExtensionTypeInfo { + vector modifiers; + unordered_map properties; + +public: + void Serialize(Serializer &serializer) const; + static unique_ptr Deserialize(Deserializer &source); + bool Equals(optional_ptr other_p) const; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/extra_type_info.hpp b/src/duckdb/src/include/duckdb/common/extra_type_info.hpp index 7ed7197b5..8e9afe0d2 100644 --- a/src/duckdb/src/include/duckdb/common/extra_type_info.hpp +++ b/src/duckdb/src/include/duckdb/common/extra_type_info.hpp @@ -10,6 +10,7 @@ #include "duckdb/common/common.hpp" #include "duckdb/common/types/vector.hpp" +#include "duckdb/common/extension_type_info.hpp" namespace duckdb { @@ -30,13 +31,18 @@ enum class ExtraTypeInfoType : uint8_t { }; struct ExtraTypeInfo { + ExtraTypeInfoType type; + string alias; + unique_ptr extension_info; + explicit ExtraTypeInfo(ExtraTypeInfoType type); explicit ExtraTypeInfo(ExtraTypeInfoType type, string alias); virtual ~ExtraTypeInfo(); - ExtraTypeInfoType type; - string alias; - vector modifiers; +protected: + // copy constructor (protected) + ExtraTypeInfo(const ExtraTypeInfo &other); + ExtraTypeInfo &operator=(const ExtraTypeInfo &other); public: bool Equals(ExtraTypeInfo *other_p) const; diff --git a/src/duckdb/src/include/duckdb/common/types.hpp b/src/duckdb/src/include/duckdb/common/types.hpp index 216e1a361..1bc43811a 100644 --- a/src/duckdb/src/include/duckdb/common/types.hpp +++ b/src/duckdb/src/include/duckdb/common/types.hpp @@ -234,6 +234,7 @@ enum class LogicalTypeId : uint8_t { }; struct ExtraTypeInfo; +struct ExtensionTypeInfo; struct aggregate_state_t; // NOLINT: mimic std casing @@ -323,11 +324,11 @@ struct LogicalType { DUCKDB_API void SetAlias(string alias); DUCKDB_API bool HasAlias() const; DUCKDB_API string GetAlias() const; - DUCKDB_API void SetModifiers(vector modifiers); - DUCKDB_API bool HasModifiers() const; - DUCKDB_API vector GetModifiersCopy() const; - DUCKDB_API optional_ptr> GetModifiers(); - DUCKDB_API optional_ptr> GetModifiers() const; + + DUCKDB_API bool HasExtensionInfo() const; + DUCKDB_API optional_ptr GetExtensionInfo() const; + DUCKDB_API optional_ptr GetExtensionInfo(); + DUCKDB_API void SetExtensionInfo(unique_ptr info); //! Returns the maximum logical type when combining the two types - or throws an exception if combining is not possible DUCKDB_API static LogicalType MaxLogicalType(ClientContext &context, const LogicalType &left, const LogicalType &right); diff --git a/src/duckdb/src/include/duckdb/function/table/arrow.hpp b/src/duckdb/src/include/duckdb/function/table/arrow.hpp index 4f8f24f3d..7db6cd363 100644 --- a/src/duckdb/src/include/duckdb/function/table/arrow.hpp +++ b/src/duckdb/src/include/duckdb/function/table/arrow.hpp @@ -190,7 +190,8 @@ struct ArrowTableFunction { vector &return_types, vector &names); //! Actual conversion from Arrow to DuckDB static void ArrowToDuckDB(ArrowScanLocalState &scan_state, const arrow_column_map_t &arrow_convert_data, - DataChunk &output, idx_t start, bool arrow_scan_is_projected = true); + DataChunk &output, idx_t start, bool arrow_scan_is_projected = true, + idx_t rowid_column_index = COLUMN_IDENTIFIER_ROW_ID); //! Get next scan state static bool ArrowScanParallelStateNext(ClientContext &context, const FunctionData *bind_data_p, diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp index 993863c11..0a73d12cb 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp @@ -23,7 +23,8 @@ class LogicalGet : public LogicalOperator { public: LogicalGet(idx_t table_index, TableFunction function, unique_ptr bind_data, - vector returned_types, vector returned_names); + vector returned_types, vector returned_names, + LogicalType rowid_type = LogicalType(LogicalType::ROW_TYPE)); //! The table index in the current bind context idx_t table_index; @@ -79,6 +80,10 @@ class LogicalGet : public LogicalOperator { void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); + LogicalType GetRowIdType() const { + return rowid_type; + } + protected: void ResolveTypes() override; @@ -88,5 +93,8 @@ class LogicalGet : public LogicalOperator { private: //! Bound column IDs vector column_ids; + + //! The type of the rowid column + LogicalType rowid_type = LogicalType(LogicalType::ROW_TYPE); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/table_binding.hpp b/src/duckdb/src/include/duckdb/planner/table_binding.hpp index 4b9030b47..50631f57a 100644 --- a/src/duckdb/src/include/duckdb/planner/table_binding.hpp +++ b/src/duckdb/src/include/duckdb/planner/table_binding.hpp @@ -33,7 +33,8 @@ enum class BindingType { BASE, TABLE, DUMMY, CATALOG_ENTRY }; //! A Binding represents a binding to a table, table-producing function or subquery with a specified table index. struct Binding { - Binding(BindingType binding_type, BindingAlias alias, vector types, vector names, idx_t index); + Binding(BindingType binding_type, BindingAlias alias, vector types, vector names, idx_t index, + LogicalType rowid_type = LogicalType(LogicalType::ROW_TYPE)); virtual ~Binding() = default; //! The type of Binding @@ -49,6 +50,8 @@ struct Binding { //! Name -> index for the names case_insensitive_map_t name_map; + LogicalType rowid_type; + public: bool TryGetBindingIndex(const string &column_name, column_t &column_index); column_t GetBindingIndex(const string &column_name); diff --git a/src/duckdb/src/main/config.cpp b/src/duckdb/src/main/config.cpp index 5788d296e..2c604cec3 100644 --- a/src/duckdb/src/main/config.cpp +++ b/src/duckdb/src/main/config.cpp @@ -548,8 +548,9 @@ idx_t DBConfig::ParseMemoryLimit(const string &arg) { } else if (unit == "tib") { multiplier = 1024LL * 1024LL * 1024LL * 1024LL; } else { - throw ParserException("Unknown unit for memory_limit: %s (expected: KB, MB, GB, TB for 1000^i units or KiB, " - "MiB, GiB, TiB for 1024^i unites)"); + throw ParserException("Unknown unit for memory_limit: '%s' (expected: KB, MB, GB, TB for 1000^i units or KiB, " + "MiB, GiB, TiB for 1024^i units)", + unit); } return LossyNumericCast(static_cast(multiplier) * limit); } diff --git a/src/duckdb/src/main/settings/custom_settings.cpp b/src/duckdb/src/main/settings/custom_settings.cpp index e8de192ab..57e961a22 100644 --- a/src/duckdb/src/main/settings/custom_settings.cpp +++ b/src/duckdb/src/main/settings/custom_settings.cpp @@ -895,6 +895,10 @@ Value MaxMemorySetting::GetSetting(const ClientContext &context) { // Max Temp Directory Size //===----------------------------------------------------------------------===// void MaxTempDirectorySizeSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input == "90% of available disk space") { + ResetGlobal(db, config); + return; + } auto maximum_swap_space = DBConfig::ParseMemoryLimit(input.ToString()); if (maximum_swap_space == DConstants::INVALID_INDEX) { // We use INVALID_INDEX to indicate that the value is not set by the user diff --git a/src/duckdb/src/parser/transform/helpers/transform_typename.cpp b/src/duckdb/src/parser/transform/helpers/transform_typename.cpp index bd6b97a10..a7beb3ba2 100644 --- a/src/duckdb/src/parser/transform/helpers/transform_typename.cpp +++ b/src/duckdb/src/parser/transform/helpers/transform_typename.cpp @@ -57,14 +57,15 @@ vector Transformer::TransformTypeModifiers(duckdb_libpgquery::PGTypeName if (type_name.typmods) { for (auto node = type_name.typmods->head; node; node = node->next) { if (type_mods.size() > 9) { - auto name = PGPointerCast(type_name.names->tail->data.ptr_value)->val.str; + const auto &name = + *PGPointerCast(type_name.names->tail->data.ptr_value)->val.str; throw ParserException("'%s': a maximum of 9 type modifiers is allowed", name); } - auto &const_val = *PGPointerCast(node->data.ptr_value); + const auto &const_val = *PGPointerCast(node->data.ptr_value); if (const_val.type != duckdb_libpgquery::T_PGAConst) { throw ParserException("Expected a constant as type modifier"); } - auto const_expr = TransformValue(const_val.val); + const auto const_expr = TransformValue(const_val.val); type_mods.push_back(std::move(const_expr->value)); } } diff --git a/src/duckdb/src/planner/binder/statement/bind_create.cpp b/src/duckdb/src/planner/binder/statement/bind_create.cpp index 156be1e5d..495a687e6 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create.cpp @@ -40,6 +40,7 @@ #include "duckdb/planner/query_node/bound_select_node.hpp" #include "duckdb/planner/tableref/bound_basetableref.hpp" #include "duckdb/storage/storage_extension.hpp" +#include "duckdb/common/extension_type_info.hpp" namespace duckdb { @@ -253,139 +254,145 @@ static bool IsValidUserType(optional_ptr entry) { } void Binder::BindLogicalType(LogicalType &type, optional_ptr catalog, const string &schema) { - if (type.id() == LogicalTypeId::LIST || type.id() == LogicalTypeId::MAP) { - auto child_type = ListType::GetChildType(type); - BindLogicalType(child_type, catalog, schema); + if (type.id() != LogicalTypeId::USER) { + // Recursive types, make sure to bind any nested user types recursively auto alias = type.GetAlias(); - auto modifiers = type.GetModifiersCopy(); - if (type.id() == LogicalTypeId::LIST) { + auto ext_info = type.HasExtensionInfo() ? make_uniq(*type.GetExtensionInfo()) : nullptr; + + switch (type.id()) { + case LogicalTypeId::LIST: { + auto child_type = ListType::GetChildType(type); + BindLogicalType(child_type, catalog, schema); type = LogicalType::LIST(child_type); - } else { - D_ASSERT(child_type.id() == LogicalTypeId::STRUCT); // map must be list of structs - type = LogicalType::MAP(child_type); + } break; + case LogicalTypeId::MAP: { + auto key_type = MapType::KeyType(type); + BindLogicalType(key_type, catalog, schema); + auto value_type = MapType::ValueType(type); + BindLogicalType(value_type, catalog, schema); + type = LogicalType::MAP(key_type, value_type); + } break; + case LogicalTypeId::ARRAY: { + auto child_type = ArrayType::GetChildType(type); + auto array_size = ArrayType::GetSize(type); + BindLogicalType(child_type, catalog, schema); + type = LogicalType::ARRAY(child_type, array_size); + } break; + case LogicalTypeId::STRUCT: { + auto child_types = StructType::GetChildTypes(type); + for (auto &child_type : child_types) { + BindLogicalType(child_type.second, catalog, schema); + } + type = LogicalType::STRUCT(child_types); + } break; + case LogicalTypeId::UNION: { + auto member_types = UnionType::CopyMemberTypes(type); + for (auto &member_type : member_types) { + BindLogicalType(member_type.second, catalog, schema); + } + type = LogicalType::UNION(member_types); + } break; + default: + break; } + // Set the alias and extension info back type.SetAlias(alias); - type.SetModifiers(modifiers); - } else if (type.id() == LogicalTypeId::STRUCT) { - auto child_types = StructType::GetChildTypes(type); - for (auto &child_type : child_types) { - BindLogicalType(child_type.second, catalog, schema); + type.SetExtensionInfo(std::move(ext_info)); + + return; + } + + // User type, bind the user type + auto user_type_name = UserType::GetTypeName(type); + auto user_type_schema = UserType::GetSchema(type); + auto user_type_mods = UserType::GetTypeModifiers(type); + + bind_type_modifiers_function_t user_bind_modifiers_func = nullptr; + + if (catalog) { + // The search order is: + // 1) In the explicitly set schema (my_schema.my_type) + // 2) In the same schema as the table + // 3) In the same catalog + // 4) System catalog + + optional_ptr entry = nullptr; + if (!user_type_schema.empty()) { + entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, *catalog, user_type_schema, user_type_name, + OnEntryNotFound::RETURN_NULL); } - // Generate new Struct Type - auto alias = type.GetAlias(); - auto modifiers = type.GetModifiersCopy(); - type = LogicalType::STRUCT(child_types); - type.SetAlias(alias); - type.SetModifiers(modifiers); - } else if (type.id() == LogicalTypeId::ARRAY) { - auto child_type = ArrayType::GetChildType(type); - auto array_size = ArrayType::GetSize(type); - BindLogicalType(child_type, catalog, schema); - auto alias = type.GetAlias(); - auto modifiers = type.GetModifiersCopy(); - type = LogicalType::ARRAY(child_type, array_size); - type.SetAlias(alias); - type.SetModifiers(modifiers); - } else if (type.id() == LogicalTypeId::UNION) { - auto member_types = UnionType::CopyMemberTypes(type); - for (auto &member_type : member_types) { - BindLogicalType(member_type.second, catalog, schema); + if (!IsValidUserType(entry)) { + entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, *catalog, schema, user_type_name, + OnEntryNotFound::RETURN_NULL); } - // Generate new Union Type - auto alias = type.GetAlias(); - auto modifiers = type.GetModifiersCopy(); - type = LogicalType::UNION(member_types); - type.SetAlias(alias); - type.SetModifiers(modifiers); - } else if (type.id() == LogicalTypeId::USER) { - auto user_type_name = UserType::GetTypeName(type); - auto user_type_schema = UserType::GetSchema(type); - auto user_type_mods = UserType::GetTypeModifiers(type); - - bind_type_modifiers_function_t user_bind_modifiers_func = nullptr; - - if (catalog) { - // The search order is: - // 1) In the explicitly set schema (my_schema.my_type) - // 2) In the same schema as the table - // 3) In the same catalog - // 4) System catalog - - optional_ptr entry = nullptr; - if (!user_type_schema.empty()) { - entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, *catalog, user_type_schema, user_type_name, - OnEntryNotFound::RETURN_NULL); - } - if (!IsValidUserType(entry)) { - entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, *catalog, schema, user_type_name, - OnEntryNotFound::RETURN_NULL); - } - if (!IsValidUserType(entry)) { - entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, *catalog, INVALID_SCHEMA, user_type_name, - OnEntryNotFound::RETURN_NULL); - } - if (!IsValidUserType(entry)) { - entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, INVALID_CATALOG, INVALID_SCHEMA, - user_type_name, OnEntryNotFound::THROW_EXCEPTION); - } - auto &type_entry = entry->Cast(); - type = type_entry.user_type; - user_bind_modifiers_func = type_entry.bind_modifiers; - } else { - string type_catalog = UserType::GetCatalog(type); - string type_schema = UserType::GetSchema(type); - - BindSchemaOrCatalog(context, type_catalog, type_schema); - auto entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, type_catalog, type_schema, user_type_name); - auto &type_entry = entry->Cast(); - type = type_entry.user_type; - user_bind_modifiers_func = type_entry.bind_modifiers; + if (!IsValidUserType(entry)) { + entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, *catalog, INVALID_SCHEMA, user_type_name, + OnEntryNotFound::RETURN_NULL); + } + if (!IsValidUserType(entry)) { + entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, INVALID_CATALOG, INVALID_SCHEMA, user_type_name, + OnEntryNotFound::THROW_EXCEPTION); } + auto &type_entry = entry->Cast(); + type = type_entry.user_type; + user_bind_modifiers_func = type_entry.bind_modifiers; + } else { + string type_catalog = UserType::GetCatalog(type); + string type_schema = UserType::GetSchema(type); + + BindSchemaOrCatalog(context, type_catalog, type_schema); + auto entry = entry_retriever.GetEntry(CatalogType::TYPE_ENTRY, type_catalog, type_schema, user_type_name); + auto &type_entry = entry->Cast(); + type = type_entry.user_type; + user_bind_modifiers_func = type_entry.bind_modifiers; + } - BindLogicalType(type, catalog, schema); - - // Apply the type modifiers (if any) - if (user_bind_modifiers_func) { - // If an explicit bind_modifiers function was provided, use that to set the type modifier - BindTypeModifiersInput input {context, type, user_type_mods}; - type = user_bind_modifiers_func(input); - } else if (type.HasModifiers()) { - // If the type already has modifiers, try to replace them with the user-provided ones if they are compatible - // This enables registering custom types with "default" type modifiers that can be overridden, without - // having to provide a custom bind_modifiers function - auto type_mods_size = type.GetModifiers()->size(); - - // Are we trying to pass more type modifiers than the type has? - if (user_type_mods.size() > type_mods_size) { - throw BinderException( - "Cannot apply '%d' type modifier(s) to type '%s' taking at most '%d' type modifier(s)", - user_type_mods.size(), user_type_name, type_mods_size); - } + // Now we bind the inner user type + BindLogicalType(type, catalog, schema); - // Deep copy the type so that we can replace the type modifiers - type = type.DeepCopy(); - - // Re-fetch the type modifiers now that we've deduplicated the ExtraTypeInfo - auto &type_mods = *type.GetModifiers(); - - // Replace them in order, casting if necessary - for (idx_t i = 0; i < MinValue(type_mods.size(), user_type_mods.size()); i++) { - auto &type_mod = type_mods[i]; - auto user_type_mod = user_type_mods[i]; - if (type_mod.type() == user_type_mod.type()) { - type_mod = std::move(user_type_mod); - } else if (user_type_mod.DefaultTryCastAs(type_mod.type())) { - type_mod = std::move(user_type_mod); - } else { - throw BinderException("Cannot apply type modifier '%s' to type '%s', expected value of type '%s'", - user_type_mod.ToString(), user_type_name, type_mod.type().ToString()); - } + // Apply the type modifiers (if any) + if (user_bind_modifiers_func) { + // If an explicit bind_modifiers function was provided, use that to construct the type + BindTypeModifiersInput input {context, type, user_type_mods}; + type = user_bind_modifiers_func(input); + } else if (type.HasExtensionInfo()) { + auto &ext_info = *type.GetExtensionInfo(); + + // If the type already has modifiers, try to replace them with the user-provided ones if they are compatible + // This enables registering custom types with "default" type modifiers that can be overridden, without + // having to provide a custom bind_modifiers function + auto type_mods_size = ext_info.modifiers.size(); + + // Are we trying to pass more type modifiers than the type has? + if (user_type_mods.size() > type_mods_size) { + throw BinderException( + "Cannot apply '%d' type modifier(s) to type '%s' taking at most '%d' type modifier(s)", + user_type_mods.size(), user_type_name, type_mods_size); + } + + // Deep copy the type so that we can replace the type modifiers + type = type.DeepCopy(); + + // Re-fetch the type modifiers now that we've deduplicated the ExtraTypeInfo + auto &type_mods = type.GetExtensionInfo()->modifiers; + + // Replace them in order, casting if necessary + for (idx_t i = 0; i < MinValue(type_mods.size(), user_type_mods.size()); i++) { + auto &type_mod = type_mods[i]; + auto user_type_mod = user_type_mods[i]; + if (type_mod.type() == user_type_mod.type()) { + type_mod = std::move(user_type_mod); + } else if (user_type_mod.DefaultTryCastAs(type_mod.type())) { + type_mod = std::move(user_type_mod); + } else { + throw BinderException("Cannot apply type modifier '%s' to type '%s', expected value of type '%s'", + user_type_mod.ToString(), user_type_name, type_mod.type().ToString()); } - } else if (!user_type_mods.empty()) { - // We're trying to pass type modifiers to a type that doesnt have any - throw BinderException("Type '%s' does not take any type modifiers", user_type_name); } + } else if (!user_type_mods.empty()) { + // We're trying to pass type modifiers to a type that doesnt have any + throw BinderException("Type '%s' does not take any type modifiers", user_type_name); } } diff --git a/src/duckdb/src/planner/binder/statement/bind_delete.cpp b/src/duckdb/src/planner/binder/statement/bind_delete.cpp index cb9457d70..822f487ed 100644 --- a/src/duckdb/src/planner/binder/statement/bind_delete.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_delete.cpp @@ -76,7 +76,7 @@ BoundStatement Binder::Bind(DeleteStatement &stmt) { // set up the delete expression auto &column_ids = get.GetColumnIds(); del->expressions.push_back( - make_uniq(LogicalType::ROW_TYPE, ColumnBinding(get.table_index, column_ids.size()))); + make_uniq(table.GetRowIdType(), ColumnBinding(get.table_index, column_ids.size()))); get.AddColumnId(COLUMN_IDENTIFIER_ROW_ID); if (!stmt.returning_list.empty()) { diff --git a/src/duckdb/src/planner/binder/statement/bind_update.cpp b/src/duckdb/src/planner/binder/statement/bind_update.cpp index ba25497ed..75dd39074 100644 --- a/src/duckdb/src/planner/binder/statement/bind_update.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_update.cpp @@ -135,7 +135,7 @@ BoundStatement Binder::Bind(UpdateStatement &stmt) { // finally add the row id column to the projection list auto &column_ids = get->GetColumnIds(); proj->expressions.push_back( - make_uniq(LogicalType::ROW_TYPE, ColumnBinding(get->table_index, column_ids.size()))); + make_uniq(table.GetRowIdType(), ColumnBinding(get->table_index, column_ids.size()))); get->AddColumnId(COLUMN_IDENTIFIER_ROW_ID); // set the projection as child of the update node and finalize the result diff --git a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp index ba41b051e..f2566cf71 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp @@ -247,8 +247,9 @@ unique_ptr Binder::Bind(BaseTableRef &ref) { } table_names = BindContext::AliasColumnNames(ref.table_name, table_names, ref.column_name_alias); - auto logical_get = make_uniq(table_index, scan_function, std::move(bind_data), - std::move(return_types), std::move(return_names)); + auto logical_get = + make_uniq(table_index, scan_function, std::move(bind_data), std::move(return_types), + std::move(return_names), table.GetRowIdType()); auto table_entry = logical_get->GetTable(); auto &col_ids = logical_get->GetMutableColumnIds(); if (!table_entry) { diff --git a/src/duckdb/src/planner/expression/bound_window_expression.cpp b/src/duckdb/src/planner/expression/bound_window_expression.cpp index 2701bd57a..5f778db14 100644 --- a/src/duckdb/src/planner/expression/bound_window_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_window_expression.cpp @@ -233,7 +233,7 @@ unique_ptr BoundWindowExpression::Deserialize(Deserializer &deserial deserializer.ReadPropertyWithExplicitDefault(211, "default_expr", result->default_expr, unique_ptr()); deserializer.ReadProperty(212, "exclude_clause", result->exclude_clause); deserializer.ReadProperty(213, "distinct", result->distinct); - deserializer.ReadProperty(214, "arg_orders", result->arg_orders); + deserializer.ReadPropertyWithExplicitDefault(214, "arg_orders", result->arg_orders, vector()); return std::move(result); } diff --git a/src/duckdb/src/planner/operator/logical_get.cpp b/src/duckdb/src/planner/operator/logical_get.cpp index 97dbe3f69..be7b5aa5d 100644 --- a/src/duckdb/src/planner/operator/logical_get.cpp +++ b/src/duckdb/src/planner/operator/logical_get.cpp @@ -17,10 +17,10 @@ LogicalGet::LogicalGet() : LogicalOperator(LogicalOperatorType::LOGICAL_GET) { } LogicalGet::LogicalGet(idx_t table_index, TableFunction function, unique_ptr bind_data, - vector returned_types, vector returned_names) + vector returned_types, vector returned_names, LogicalType rowid_type) : LogicalOperator(LogicalOperatorType::LOGICAL_GET), table_index(table_index), function(std::move(function)), bind_data(std::move(bind_data)), returned_types(std::move(returned_types)), names(std::move(returned_names)), - extra_info() { + extra_info(), rowid_type(std::move(rowid_type)) { } optional_ptr LogicalGet::GetTable() const { @@ -126,7 +126,7 @@ void LogicalGet::ResolveTypes() { if (projection_ids.empty()) { for (auto &index : column_ids) { if (index.IsRowIdColumn()) { - types.emplace_back(LogicalType::ROW_TYPE); + types.emplace_back(LogicalType(rowid_type)); } else { types.push_back(returned_types[index.GetPrimaryIndex()]); } @@ -135,7 +135,7 @@ void LogicalGet::ResolveTypes() { for (auto &proj_index : projection_ids) { auto &index = column_ids[proj_index]; if (index.IsRowIdColumn()) { - types.emplace_back(LogicalType::ROW_TYPE); + types.emplace_back(LogicalType(rowid_type)); } else { types.push_back(returned_types[index.GetPrimaryIndex()]); } diff --git a/src/duckdb/src/planner/table_binding.cpp b/src/duckdb/src/planner/table_binding.cpp index 119db130d..455834a4b 100644 --- a/src/duckdb/src/planner/table_binding.cpp +++ b/src/duckdb/src/planner/table_binding.cpp @@ -16,9 +16,9 @@ namespace duckdb { Binding::Binding(BindingType binding_type, BindingAlias alias_p, vector coltypes, vector colnames, - idx_t index) + idx_t index, LogicalType rowid_type) : binding_type(binding_type), alias(std::move(alias_p)), index(index), types(std::move(coltypes)), - names(std::move(colnames)) { + names(std::move(colnames)), rowid_type(std::move(rowid_type)) { D_ASSERT(types.size() == names.size()); for (idx_t i = 0; i < names.size(); i++) { auto &name = names[i]; @@ -115,7 +115,8 @@ optional_ptr EntryBinding::GetStandardEntry() { TableBinding::TableBinding(const string &alias, vector types_p, vector names_p, vector &bound_column_ids, optional_ptr entry, idx_t index, bool add_row_id) - : Binding(BindingType::TABLE, GetAlias(alias, entry), std::move(types_p), std::move(names_p), index), + : Binding(BindingType::TABLE, GetAlias(alias, entry), std::move(types_p), std::move(names_p), index, + (add_row_id && entry) ? entry->Cast().GetRowIdType() : LogicalType::ROW_TYPE), bound_column_ids(bound_column_ids), entry(entry) { if (add_row_id) { if (name_map.find("rowid") == name_map.end()) { @@ -238,7 +239,7 @@ BindResult TableBinding::Bind(ColumnRefExpression &colref, idx_t depth) { // fetch the type of the column LogicalType col_type; if (column_index == COLUMN_IDENTIFIER_ROW_ID) { - col_type = LogicalType::ROW_TYPE; + col_type = LogicalType(rowid_type); } else { // normal column: fetch type from base column col_type = types[column_index]; diff --git a/src/duckdb/src/storage/serialization/serialize_types.cpp b/src/duckdb/src/storage/serialization/serialize_types.cpp index dbbd0d455..34f48bdec 100644 --- a/src/duckdb/src/storage/serialization/serialize_types.cpp +++ b/src/duckdb/src/storage/serialization/serialize_types.cpp @@ -5,6 +5,7 @@ #include "duckdb/common/serializer/serializer.hpp" #include "duckdb/common/serializer/deserializer.hpp" +#include "duckdb/common/extension_type_info.hpp" #include "duckdb/common/extra_type_info.hpp" namespace duckdb { @@ -12,13 +13,15 @@ namespace duckdb { void ExtraTypeInfo::Serialize(Serializer &serializer) const { serializer.WriteProperty(100, "type", type); serializer.WritePropertyWithDefault(101, "alias", alias); - serializer.WritePropertyWithDefault>(102, "modifiers", modifiers, vector()); + /* [Deleted] (vector) "modifiers" */ + serializer.WritePropertyWithDefault>(103, "extension_info", extension_info); } shared_ptr ExtraTypeInfo::Deserialize(Deserializer &deserializer) { auto type = deserializer.ReadProperty(100, "type"); auto alias = deserializer.ReadPropertyWithDefault(101, "alias"); - auto modifiers = deserializer.ReadPropertyWithExplicitDefault>(102, "modifiers", vector()); + deserializer.ReadDeletedProperty>(102, "modifiers"); + auto extension_info = deserializer.ReadPropertyWithDefault>(103, "extension_info"); shared_ptr result; switch (type) { case ExtraTypeInfoType::AGGREGATE_STATE_TYPE_INFO: @@ -60,7 +63,7 @@ shared_ptr ExtraTypeInfo::Deserialize(Deserializer &deserializer) throw SerializationException("Unsupported type for deserialization of ExtraTypeInfo!"); } result->alias = std::move(alias); - result->modifiers = std::move(modifiers); + result->extension_info = std::move(extension_info); return result; } @@ -118,6 +121,18 @@ shared_ptr DecimalTypeInfo::Deserialize(Deserializer &deserialize return std::move(result); } +void ExtensionTypeInfo::Serialize(Serializer &serializer) const { + serializer.WritePropertyWithDefault>(100, "modifiers", modifiers); + serializer.WritePropertyWithDefault>(101, "properties", properties, unordered_map()); +} + +unique_ptr ExtensionTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new ExtensionTypeInfo()); + deserializer.ReadPropertyWithDefault>(100, "modifiers", result->modifiers); + deserializer.ReadPropertyWithExplicitDefault>(101, "properties", result->properties, unordered_map()); + return result; +} + void IntegerLiteralTypeInfo::Serialize(Serializer &serializer) const { ExtraTypeInfo::Serialize(serializer); serializer.WriteProperty(200, "constant_value", constant_value); @@ -167,7 +182,7 @@ void UserTypeInfo::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(200, "user_type_name", user_type_name); serializer.WritePropertyWithDefault(201, "catalog", catalog, string()); serializer.WritePropertyWithDefault(202, "schema", schema, string()); - serializer.WritePropertyWithDefault>(203, "user_type_modifiers", user_type_modifiers, vector()); + serializer.WritePropertyWithDefault>(203, "user_type_modifiers", user_type_modifiers); } shared_ptr UserTypeInfo::Deserialize(Deserializer &deserializer) { @@ -175,7 +190,7 @@ shared_ptr UserTypeInfo::Deserialize(Deserializer &deserializer) deserializer.ReadPropertyWithDefault(200, "user_type_name", result->user_type_name); deserializer.ReadPropertyWithExplicitDefault(201, "catalog", result->catalog, string()); deserializer.ReadPropertyWithExplicitDefault(202, "schema", result->schema, string()); - deserializer.ReadPropertyWithExplicitDefault>(203, "user_type_modifiers", result->user_type_modifiers, vector()); + deserializer.ReadPropertyWithDefault>(203, "user_type_modifiers", result->user_type_modifiers); return std::move(result); }