@@ -37,6 +37,7 @@ R_altrep_class_t RelToAltrep::string_class;
3737
3838#if defined(R_HAS_ALTLIST)
3939R_altrep_class_t RelToAltrep::list_class;
40+ R_altrep_class_t RelToAltrep::struct_class;
4041#endif
4142
4243const size_t MAX_SIZE_T = std::numeric_limits<size_t >::max();
@@ -77,6 +78,12 @@ void RelToAltrep::Initialize(DllInfo *dll) {
7778 R_set_altrep_Length_method (list_class, VectorLength);
7879 R_set_altvec_Dataptr_method (list_class, VectorDataptr);
7980 R_set_altlist_Elt_method (list_class, VectorListElt);
81+
82+ struct_class = R_make_altlist_class (" reltoaltrep_struct_class" , " duckdb" , dll);
83+ R_set_altrep_Inspect_method (struct_class, RelInspect);
84+ R_set_altrep_Length_method (struct_class, StructLength);
85+ R_set_altvec_Dataptr_method (struct_class, VectorDataptr);
86+ R_set_altlist_Elt_method (struct_class, VectorListElt);
8087#endif
8188}
8289
@@ -340,6 +347,17 @@ SEXP RelToAltrep::VectorStringElt(SEXP x, R_xlen_t i) {
340347}
341348
342349#if defined(R_HAS_ALTLIST)
350+ R_xlen_t RelToAltrep::StructLength (SEXP x) {
351+ BEGIN_CPP11
352+ auto const *wrapper = AltrepVectorWrapper::Get (x);
353+ auto const column_index = wrapper->column_index ;
354+ auto const &res = wrapper->rel ->GetQueryResult ();
355+ auto const &type = res->types [column_index];
356+
357+ return static_cast <R_xlen_t>(StructType::GetChildTypes (type).size ());
358+ END_CPP11_EX (0 )
359+ }
360+
343361SEXP RelToAltrep::VectorListElt (SEXP x, R_xlen_t i) {
344362 BEGIN_CPP11
345363 return VECTOR_ELT (AltrepVectorWrapper::Get (x)->Vector (), i);
@@ -360,7 +378,11 @@ static R_altrep_class_t LogicalTypeToAltrepType(const LogicalType &type, const d
360378 return RelToAltrep::string_class;
361379#if defined(R_HAS_ALTLIST)
362380 case VECSXP:
363- return RelToAltrep::list_class;
381+ if (type.id () == LogicalTypeId::STRUCT) {
382+ return RelToAltrep::struct_class;
383+ } else {
384+ return RelToAltrep::list_class;
385+ }
364386#endif
365387
366388 default :
@@ -407,6 +429,15 @@ size_t DoubleToSize(double d) {
407429
408430 cpp11::sexp vector_sexp = R_new_altrep (LogicalTypeToAltrepType (col_type, col_name), ptr, R_NilValue);
409431 duckdb_r_decorate (col_type, vector_sexp, duckdb::ConvertOpts ());
432+
433+ // Special case: Only STRUCTs have a redundant row names attribute
434+ // Moving this logic into duckdb_r_decorate() would add too much noise elsewhere
435+ if (col_type.id () == LogicalTypeId::STRUCT) {
436+ // FIXME: The exact class of nested columns can be a property
437+ // of the relation object, determined by the data on input
438+ duckdb_r_df_decorate_impl (vector_sexp, row_names_sexp, RStrings::get ().dataframe_str );
439+ }
440+
410441 data_frame.push_back (vector_sexp);
411442 }
412443
0 commit comments