@@ -29,8 +29,6 @@ const (
29
29
//
30
30
// - Dynamic columns are supported but only for maps and struct slices
31
31
//
32
- // - Repeated Columns are not supported
33
- //
34
32
// - Nested Columns are not supported
35
33
//
36
34
// # Tags
@@ -87,6 +85,15 @@ const (
87
85
// // Use supported tags to customize the column value
88
86
// Labels []Label `frostdb:"labels,dyn"`
89
87
// }
88
+ //
89
+ // # Repeated columns
90
+ //
91
+ // Fields of type []int64, []float64, []bool, and []string are supported. These
92
+ // are represented as arrow.LIST.
93
+ //
94
+ // Generated schema for the repeated columns applies all supported tags. By
95
+ // default repeated fields are nullable. You can safely pass nil slices for
96
+ // repeated columns.
90
97
type Build [T any ] struct {
91
98
fields []* fieldRecord
92
99
buffer []arrow.Array
@@ -208,7 +215,13 @@ func NewBuild[T any](mem memory.Allocator) *Build[T] {
208
215
fr .nullable = true
209
216
fr .build = newMapFieldBuilder (newFieldFunc (typ , mem , name ))
210
217
default :
211
- panic ("frostdb/dynschema: repeated columns not supported " )
218
+ typ , styp = baseType (fty .Elem (), dictionary )
219
+ fr .typ = styp
220
+ fr .repeated = true
221
+ // Repeated columns are always nullable
222
+ fr .nullable = true
223
+ typ = arrow .ListOf (typ )
224
+ fr .build = newFieldBuild (typ , mem , name , true )
212
225
}
213
226
case reflect .Int64 , reflect .Float64 , reflect .Bool , reflect .String :
214
227
typ , styp = baseType (fty , dictionary )
@@ -270,6 +283,7 @@ func (b Build[T]) Schema(name string) (s *schemapb.Schema) {
270
283
Encoding : f .encoding ,
271
284
Compression : f .compression ,
272
285
Nullable : f .nullable ,
286
+ Repeated : f .repeated ,
273
287
},
274
288
})
275
289
if f .sort {
@@ -530,12 +544,133 @@ func newFieldBuild(dt arrow.DataType, mem memory.Allocator, name string, nullabl
530
544
f .buildFunc = func (v reflect.Value ) error {
531
545
return e .AppendString (v .Interface ().(string ))
532
546
}
547
+ case * array.ListBuilder :
548
+ switch build := e .ValueBuilder ().(type ) {
549
+ case * array.Int64Builder :
550
+ f .buildFunc = func (v reflect.Value ) error {
551
+ if v .IsNil () {
552
+ e .AppendNull ()
553
+ return nil
554
+ }
555
+ e .Append (true )
556
+ build .Reserve (v .Len ())
557
+ return applyInt (v , func (i int64 ) error {
558
+ build .Append (i )
559
+ return nil
560
+ })
561
+ }
562
+ case * array.Int64DictionaryBuilder :
563
+ f .buildFunc = func (v reflect.Value ) error {
564
+ if v .IsNil () {
565
+ e .AppendNull ()
566
+ return nil
567
+ }
568
+ e .Append (true )
569
+ build .Reserve (v .Len ())
570
+ return applyInt (v , build .Append )
571
+ }
572
+
573
+ case * array.Float64Builder :
574
+ f .buildFunc = func (v reflect.Value ) error {
575
+ if v .IsNil () {
576
+ e .AppendNull ()
577
+ return nil
578
+ }
579
+ e .Append (true )
580
+ build .Reserve (v .Len ())
581
+ return applyFloat64 (v , func (i float64 ) error {
582
+ build .Append (i )
583
+ return nil
584
+ })
585
+ }
586
+ case * array.Float64DictionaryBuilder :
587
+ f .buildFunc = func (v reflect.Value ) error {
588
+ if v .IsNil () {
589
+ e .AppendNull ()
590
+ return nil
591
+ }
592
+ e .Append (true )
593
+ build .Reserve (v .Len ())
594
+ return applyFloat64 (v , build .Append )
595
+ }
596
+
597
+ case * array.StringBuilder :
598
+ f .buildFunc = func (v reflect.Value ) error {
599
+ if v .IsNil () {
600
+ e .AppendNull ()
601
+ return nil
602
+ }
603
+ e .Append (true )
604
+ build .Reserve (v .Len ())
605
+ return applyString (v , func (i string ) error {
606
+ build .Append (i )
607
+ return nil
608
+ })
609
+ }
610
+ case * array.BinaryDictionaryBuilder :
611
+ f .buildFunc = func (v reflect.Value ) error {
612
+ if v .Len () == 0 {
613
+ e .AppendNull ()
614
+ return nil
615
+ }
616
+ e .Append (true )
617
+ build .Reserve (v .Len ())
618
+ return applyString (v , build .AppendString )
619
+ }
620
+ case * array.BooleanBuilder :
621
+ f .buildFunc = func (v reflect.Value ) error {
622
+ if v .IsNil () {
623
+ e .AppendNull ()
624
+ return nil
625
+ }
626
+ e .Append (true )
627
+ build .Reserve (v .Len ())
628
+ return applyBool (v , func (i bool ) error {
629
+ build .Append (i )
630
+ return nil
631
+ })
632
+ }
633
+ }
533
634
default :
534
635
panic ("frostdb:dynschema: unsupported array builder " + b .Type ().String ())
535
636
}
536
637
return
537
638
}
538
639
640
+ func applyString (v reflect.Value , apply func (string ) error ) error {
641
+ return listApply [string ](v , func (v reflect.Value ) string {
642
+ return v .Interface ().(string )
643
+ }, apply )
644
+ }
645
+
646
+ func applyFloat64 (v reflect.Value , apply func (float64 ) error ) error {
647
+ return listApply [float64 ](v , func (v reflect.Value ) float64 {
648
+ return v .Float ()
649
+ }, apply )
650
+ }
651
+
652
+ func applyBool (v reflect.Value , apply func (bool ) error ) error {
653
+ return listApply [bool ](v , func (v reflect.Value ) bool {
654
+ return v .Bool ()
655
+ }, apply )
656
+ }
657
+
658
+ func applyInt (v reflect.Value , apply func (int64 ) error ) error {
659
+ return listApply [int64 ](v , func (v reflect.Value ) int64 {
660
+ return v .Int ()
661
+ }, apply )
662
+ }
663
+
664
+ func listApply [T any ](v reflect.Value , fn func (reflect.Value ) T , apply func (T ) error ) error {
665
+ for i := 0 ; i < v .Len (); i ++ {
666
+ err := apply (fn (v .Index (i )))
667
+ if err != nil {
668
+ return err
669
+ }
670
+ }
671
+ return nil
672
+ }
673
+
539
674
func newUUIDSliceField (mem memory.Allocator , name string ) (f * fieldBuilderFunc ) {
540
675
dt := & arrow.DictionaryType {
541
676
IndexType : & arrow.Int32Type {},
@@ -584,6 +719,7 @@ type fieldRecord struct {
584
719
dynamic bool
585
720
preHash bool
586
721
nullable bool
722
+ repeated bool
587
723
sort bool
588
724
nullFirst bool
589
725
sortOrder int
0 commit comments