1+ using DuckDB . NET . Native ;
2+ using System ;
3+ using System . Collections ;
4+ using System . Collections . Generic ;
5+ using System . Numerics ;
6+
7+ namespace DuckDB . NET . Data . Internal . Writer ;
8+
9+ internal sealed unsafe class ListVectorDataWriter : VectorDataWriterBase
10+ {
11+ private ulong offset = 0 ;
12+ private readonly ulong arraySize ;
13+ private readonly VectorDataWriterBase listItemWriter ;
14+
15+ public bool IsList => ColumnType == DuckDBType . List ;
16+ private ulong vectorReservedSize = DuckDBGlobalData . VectorSize ;
17+
18+ public ListVectorDataWriter ( IntPtr vector , void * vectorData , DuckDBType columnType , DuckDBLogicalType logicalType ) : base ( vector , vectorData , columnType )
19+ {
20+ using var childType = IsList ? NativeMethods . LogicalType . DuckDBListTypeChildType ( logicalType ) : NativeMethods . LogicalType . DuckDBArrayTypeChildType ( logicalType ) ;
21+ var childVector = IsList ? NativeMethods . Vectors . DuckDBListVectorGetChild ( vector ) : NativeMethods . Vectors . DuckDBArrayVectorGetChild ( vector ) ;
22+
23+ arraySize = IsList ? 0 : ( ulong ) NativeMethods . LogicalType . DuckDBArrayVectorGetSize ( logicalType ) ;
24+ listItemWriter = VectorDataWriterFactory . CreateWriter ( childVector , childType ) ;
25+ }
26+
27+ internal override bool AppendCollection ( ICollection value , int rowIndex )
28+ {
29+ var count = ( ulong ) value . Count ;
30+
31+ ResizeVector ( rowIndex % ( int ) DuckDBGlobalData . VectorSize , count ) ;
32+
33+ _ = value switch
34+ {
35+ IEnumerable < bool > items => WriteItems ( items ) ,
36+
37+ IEnumerable < sbyte > items => WriteItems ( items ) ,
38+ IEnumerable < short > items => WriteItems ( items ) ,
39+ IEnumerable < int > items => WriteItems ( items ) ,
40+ IEnumerable < long > items => WriteItems ( items ) ,
41+ IEnumerable < byte > items => WriteItems ( items ) ,
42+ IEnumerable < ushort > items => WriteItems ( items ) ,
43+ IEnumerable < uint > items => WriteItems ( items ) ,
44+ IEnumerable < ulong > items => WriteItems ( items ) ,
45+
46+ IEnumerable < float > items => WriteItems ( items ) ,
47+ IEnumerable < double > items => WriteItems ( items ) ,
48+
49+ IEnumerable < decimal > items => WriteItems ( items ) ,
50+ IEnumerable < BigInteger > items => WriteItems ( items ) ,
51+
52+ IEnumerable < string > items => WriteItems ( items ) ,
53+ IEnumerable < Guid > items => WriteItems ( items ) ,
54+ IEnumerable < DateTime > items => WriteItems ( items ) ,
55+ IEnumerable < TimeSpan > items => WriteItems ( items ) ,
56+ IEnumerable < DuckDBDateOnly > items => WriteItems ( items ) ,
57+ IEnumerable < DuckDBTimeOnly > items => WriteItems ( items ) ,
58+ #if NET6_0_OR_GREATER
59+ IEnumerable < DateOnly > items => WriteItems ( items ) ,
60+ IEnumerable< TimeOnly > items => WriteItems ( items ) ,
61+ #endif
62+ IEnumerable< DateTimeOffset > items => WriteItems ( items ) ,
63+
64+ _ => WriteItems < object > ( ( IEnumerable < object > ) value )
65+ } ;
66+
67+ var duckDBListEntry = new DuckDBListEntry ( offset , count ) ;
68+ var result = ! IsList || AppendValueInternal ( duckDBListEntry , rowIndex ) ;
69+
70+ offset += count ;
71+
72+ return result ;
73+
74+ int WriteItems < T > ( IEnumerable < T > items )
75+ {
76+ if ( IsList == false && count != arraySize )
77+ {
78+ throw new InvalidOperationException ( $ "Column has Array size of { arraySize } but the specified value has size of { count } ") ;
79+ }
80+
81+ var index = 0 ;
82+
83+ foreach ( var item in items )
84+ {
85+ listItemWriter . AppendValue ( item , ( int ) offset + ( index ++ ) ) ;
86+ }
87+
88+ return 0 ;
89+ }
90+ }
91+
92+ private void ResizeVector ( int rowIndex , ulong count )
93+ {
94+ //If writing to a list column we need to make sure that enough space is allocated. Not needed for Arrays as DuckDB does it for us.
95+ if ( ! IsList || offset + count <= vectorReservedSize ) return ;
96+
97+ var factor = 2d ;
98+
99+ if ( rowIndex > DuckDBGlobalData . VectorSize * 0.25 && rowIndex < DuckDBGlobalData . VectorSize * 0.5 )
100+ {
101+ factor = 1.75 ;
102+ }
103+
104+ if ( rowIndex > DuckDBGlobalData . VectorSize * 0.5 && rowIndex < DuckDBGlobalData . VectorSize * 0.75 )
105+ {
106+ factor = 1.5 ;
107+ }
108+
109+ if ( rowIndex > DuckDBGlobalData . VectorSize * 0.75 )
110+ {
111+ factor = 1.25 ;
112+ }
113+
114+ vectorReservedSize = ( ulong ) Math . Max ( vectorReservedSize * factor , offset + count ) ;
115+ var state = NativeMethods . Vectors . DuckDBListVectorReserve ( Vector , vectorReservedSize ) ;
116+
117+ if ( ! state . IsSuccess ( ) )
118+ {
119+ throw new DuckDBException ( $ "Failed to reserve { vectorReservedSize } for the list vector") ;
120+ }
121+
122+ listItemWriter . FetchDataPointer ( ) ;
123+ }
124+ }
0 commit comments