Skip to content

Commit 3c4528f

Browse files
committed
fix regressions and get on par with main
1 parent 8d3e3c2 commit 3c4528f

12 files changed

Lines changed: 34 additions & 32 deletions

File tree

_duckdb-stubs/_typing.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ Note:
133133
We use lowercase here to be able to reuse this `Literal` in the `DTypeIdentifiers` `Literal`.
134134
"""
135135

136-
NestedIds: TypeAlias = Literal["list", "struct", "array", "enum", "map", "decimal", "union"]
136+
NestedIds: TypeAlias = Literal["list", "struct", "tuple", "array", "enum", "map", "decimal", "union"]
137137
"""Identifiers for nested types in `DuckDBPyType.id`."""
138138

139139
PyTypeIds: TypeAlias = Builtins | NestedIds

duckdb/experimental/spark/sql/type_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def convert_nested_type(dtype: DuckDBPyType) -> DataType: # noqa: D103
9494
"DuckDB union types cannot be directly mapped to PySpark types."
9595
)
9696
raise ContributionsAcceptedError(msg)
97-
if id == "struct":
97+
if id == "struct" or id == "tuple":
9898
children: list[tuple[str, DuckDBPyType]] = dtype.children
9999
fields = [StructField(x[0], convert_type(x[1])) for x in children]
100100
return StructType(fields)
@@ -105,7 +105,7 @@ def convert_nested_type(dtype: DuckDBPyType) -> DataType: # noqa: D103
105105

106106
def convert_type(dtype: DuckDBPyType) -> DataType: # noqa: D103
107107
id = dtype.id
108-
if id in ["list", "struct", "map", "array"]:
108+
if id in ["list", "struct", "tuple", "map", "array"]:
109109
return convert_nested_type(dtype)
110110
if id == "decimal":
111111
children: list[tuple[str, DuckDBPyType]] = dtype.children

external/duckdb

Submodule duckdb updated 809 files

src/arrow/pyarrow_filter_pushdown.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,9 @@ nb::object MakePyArrowScalar(const Value &constant, const string &timezone_confi
7979
// Cast::Operation<dtime_ns_t, int64_t> for which no specialization exists, and
8080
// throws "Unimplemented type for cast (INT64 -> INT64)". Use the type-strong
8181
// GetValueUnsafe<dtime_ns_t>() which reads `value_.time_ns` from the union
82-
// directly. The `dtime_ns_t.micros` field name is a misnomer — it actually holds
83-
// nanoseconds (see arrow_conversion.cpp:432).
82+
// directly. dtime_ns_t.value holds nanoseconds (see arrow_conversion.cpp:432).
8483
nb::handle date_type = import_cache.pyarrow.time64();
85-
return dataset_scalar(scalar(constant.GetValueUnsafe<dtime_ns_t>().micros, date_type("ns")));
84+
return dataset_scalar(scalar(constant.GetValueUnsafe<dtime_ns_t>().value, date_type("ns")));
8685
}
8786
case LogicalTypeId::TIMESTAMP: {
8887
nb::handle date_type = import_cache.pyarrow.timestamp();

src/native/python_conversion.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Value TransformDictionaryToStruct(optional_ptr<ClientContext> context, const PyD
120120
const LogicalType &target_type = LogicalType::UNKNOWN) {
121121
auto struct_keys = TransformStructKeys(dict.keys, dict.len, target_type);
122122

123-
bool struct_target = target_type.id() == LogicalTypeId::STRUCT;
123+
bool struct_target = target_type.id() == LogicalTypeId::STRUCT || target_type.id() == LogicalTypeId::TUPLE;
124124
if (struct_target && dict.len != StructType::GetChildCount(target_type)) {
125125
throw InvalidInputException("We could not convert the object %s to the desired target type (%s)",
126126
dict.ToString(), target_type.ToString());
@@ -255,7 +255,7 @@ Value TransformTupleToStruct(optional_ptr<ClientContext> context, nb::handle ele
255255
auto tuple = nb::cast<nb::tuple>(ele);
256256
auto size = nb::len(tuple);
257257

258-
D_ASSERT(target_type.id() == LogicalTypeId::STRUCT);
258+
D_ASSERT(target_type.id() == LogicalTypeId::STRUCT || target_type.id() == LogicalTypeId::TUPLE);
259259
auto child_types = StructType::GetChildTypes(target_type);
260260
auto child_count = child_types.size();
261261
if (size != child_count) {
@@ -562,7 +562,7 @@ struct PythonValueConversion {
562562

563563
static void HandleTuple(optional_ptr<ClientContext> context, Value &result, const LogicalType &target_type,
564564
nb::handle ele, idx_t list_size) {
565-
if (target_type.id() == LogicalTypeId::STRUCT) {
565+
if (target_type.id() == LogicalTypeId::STRUCT || target_type.id() == LogicalTypeId::TUPLE) {
566566
result = TransformTupleToStruct(context, ele, target_type);
567567
return;
568568
}
@@ -588,6 +588,7 @@ struct PythonValueConversion {
588588
PyDictionary dict = PyDictionary(nb::borrow<nb::object>(ele));
589589
switch (target_type.id()) {
590590
case LogicalTypeId::STRUCT:
591+
case LogicalTypeId::TUPLE:
591592
return TransformDictionaryToStruct(context, dict, target_type);
592593
case LogicalTypeId::MAP:
593594
return TransformDictionaryToMap(context, dict, target_type);
@@ -890,6 +891,7 @@ struct PythonVectorConversion {
890891
auto &result_type = result.GetType();
891892
switch (result_type.id()) {
892893
case LogicalTypeId::STRUCT:
894+
case LogicalTypeId::TUPLE:
893895
ConvertTupleToStruct(context, result, result_offset, ele, tuple_size);
894896
break;
895897
case LogicalTypeId::ARRAY:
@@ -985,6 +987,7 @@ void TransformPythonObjectInternal(optional_ptr<ClientContext> context, nb::hand
985987
auto &conversion_target = OP::ConversionTarget(result, param);
986988
switch (conversion_target.id()) {
987989
case LogicalTypeId::STRUCT:
990+
case LogicalTypeId::TUPLE:
988991
case LogicalTypeId::UNKNOWN:
989992
case LogicalTypeId::LIST:
990993
case LogicalTypeId::ARRAY:

src/native/python_objects.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ static bool KeyIsHashable(const LogicalType &type) {
462462
return true;
463463
}
464464
case LogicalTypeId::STRUCT:
465+
case LogicalTypeId::TUPLE:
465466
return false;
466467
case LogicalTypeId::SQLNULL:
467468
// A SQLNULL key is always NULL, and Python's None is hashable.
@@ -608,7 +609,7 @@ nb::object PythonObject::FromValue(const Value &val, const LogicalType &type,
608609
time = val.GetValueUnsafe<dtime_t>();
609610
} else {
610611
// Python's datetime doesn't support nanoseconds, we convert to micros.
611-
time = val.GetValueUnsafe<dtime_ns_t>().time();
612+
time = dtime_t(val.GetValueUnsafe<dtime_ns_t>().value / 1000);
612613
}
613614
duckdb::Time::Convert(time, hour, min, sec, usec);
614615
try {
@@ -702,7 +703,8 @@ nb::object PythonObject::FromValue(const Value &val, const LogicalType &type,
702703
}
703704
return std::move(py_struct);
704705
}
705-
case LogicalTypeId::STRUCT: {
706+
case LogicalTypeId::STRUCT:
707+
case LogicalTypeId::TUPLE: {
706708
return FromStruct(val, type, client_properties);
707709
}
708710
case LogicalTypeId::UUID: {

src/numpy/array_wrapper.cpp

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -295,21 +295,10 @@ struct ArrayConvert {
295295
};
296296

297297
struct StructConvert {
298-
static nb::dict ConvertValue(Vector &input, idx_t chunk_offset, NumpyAppendData &append_data) {
299-
auto &client_properties = append_data.client_properties;
300-
301-
nb::dict py_struct;
298+
// Delegate to FromStruct so unnamed structs / TUPLE values become Python tuples (named ones stay dicts).
299+
static nb::object ConvertValue(Vector &input, idx_t chunk_offset, NumpyAppendData &append_data) {
302300
auto val = input.GetValue(chunk_offset);
303-
auto &child_types = StructType::GetChildTypes(input.GetType());
304-
auto &struct_children = StructValue::GetChildren(val);
305-
306-
for (idx_t i = 0; i < struct_children.size(); i++) {
307-
auto &child_entry = child_types[i];
308-
auto &child_name = child_entry.first;
309-
auto &child_type = child_entry.second;
310-
py_struct[child_name.c_str()] = PythonObject::FromValue(struct_children[i], child_type, client_properties);
311-
}
312-
return py_struct;
301+
return PythonObject::FromStruct(val, input.GetType(), append_data.client_properties);
313302
}
314303
};
315304

@@ -716,6 +705,7 @@ void ArrayWrapper::Append(idx_t current_offset, Vector &input, idx_t source_size
716705
may_have_null = ConvertNested<nb::object, duckdb_py_convert::UnionConvert>(append_data);
717706
break;
718707
case LogicalTypeId::STRUCT:
708+
case LogicalTypeId::TUPLE:
719709
may_have_null = ConvertNested<nb::object, duckdb_py_convert::StructConvert>(append_data);
720710
break;
721711
case LogicalTypeId::VARIANT:

src/numpy/raw_array_wrapper.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ static idx_t GetNumpyTypeWidth(const LogicalType &type) {
5858
case LogicalTypeId::LIST:
5959
case LogicalTypeId::MAP:
6060
case LogicalTypeId::STRUCT:
61+
case LogicalTypeId::TUPLE:
6162
case LogicalTypeId::UNION:
6263
case LogicalTypeId::UUID:
6364
case LogicalTypeId::ARRAY:
@@ -124,6 +125,7 @@ string RawArrayWrapper::DuckDBToNumpyDtype(const LogicalType &type) {
124125
case LogicalTypeId::LIST:
125126
case LogicalTypeId::MAP:
126127
case LogicalTypeId::STRUCT:
128+
case LogicalTypeId::TUPLE:
127129
case LogicalTypeId::UNION:
128130
case LogicalTypeId::UUID:
129131
case LogicalTypeId::ARRAY:

src/pyconnection.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ std::shared_ptr<DuckDBPyConnection> DuckDBPyConnection::UnregisterUDF(const stri
436436
auto &catalog = Catalog::GetCatalog(context, SYSTEM_CATALOG);
437437
DropInfo info;
438438
info.type = CatalogType::SCALAR_FUNCTION_ENTRY;
439-
info.NameMutable() = Identifier(name);
439+
info.SetName(Identifier(name));
440440
info.allow_drop_internal = true;
441441
info.cascade = false;
442442
info.if_not_found = OnEntryNotFound::THROW_EXCEPTION;
@@ -1694,7 +1694,7 @@ std::unique_ptr<DuckDBPyRelation> DuckDBPyConnection::Table(const string &tname)
16941694
auto &connection = con.GetConnection();
16951695
auto qualified_name = QualifiedName::Parse(tname);
16961696
if (qualified_name.Schema().empty()) {
1697-
qualified_name.SchemaMutable() = DEFAULT_SCHEMA;
1697+
qualified_name = QualifiedName(qualified_name.Catalog(), DEFAULT_SCHEMA, qualified_name.Name());
16981698
}
16991699
try {
17001700
return CreateRelation(

src/typing/pytype.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ bool DuckDBPyType::EqualsString(const string &type_str) const {
5555

5656
std::unique_ptr<DuckDBPyType> DuckDBPyType::GetAttribute(const string &name) const {
5757
auto name_identifier = Identifier(name);
58-
if (type.id() == LogicalTypeId::STRUCT || type.id() == LogicalTypeId::UNION) {
58+
if (type.id() == LogicalTypeId::STRUCT || type.id() == LogicalTypeId::TUPLE || type.id() == LogicalTypeId::UNION) {
5959
auto &children = StructType::GetChildTypes(type);
6060
for (idx_t i = 0; i < children.size(); i++) {
6161
auto &child = children[i];
@@ -402,6 +402,7 @@ nb::list DuckDBPyType::Children() const {
402402
switch (type.id()) {
403403
case LogicalTypeId::LIST:
404404
case LogicalTypeId::STRUCT:
405+
case LogicalTypeId::TUPLE:
405406
case LogicalTypeId::UNION:
406407
case LogicalTypeId::MAP:
407408
case LogicalTypeId::ARRAY:
@@ -436,7 +437,7 @@ nb::list DuckDBPyType::Children() const {
436437
children.append(nb::make_tuple("values", strings_list));
437438
return children;
438439
}
439-
if (id == LogicalTypeId::STRUCT || id == LogicalTypeId::UNION) {
440+
if (id == LogicalTypeId::STRUCT || id == LogicalTypeId::TUPLE || id == LogicalTypeId::UNION) {
440441
auto &struct_children = StructType::GetChildTypes(type);
441442
for (idx_t i = 0; i < struct_children.size(); i++) {
442443
auto &child = struct_children[i];

0 commit comments

Comments
 (0)