@@ -1664,66 +1664,33 @@ func must[T any](v T, err error) T {
16641664}
16651665
16661666type arrowStatsCollector struct {
1667- fieldID int
1668- schema * iceberg.Schema
1669- props iceberg.Properties
1670- defaultMode string
1667+ fieldID int
1668+ schema * iceberg.Schema
1669+ defaultMode tblutils.MetricsMode
1670+ defaultModeError error
1671+ columnModes map [string ]tblutils.MetricsMode
1672+ columnModeErrors map [string ]error
16711673}
16721674
1673- func (a * arrowStatsCollector ) Schema (_ * iceberg.Schema , results func () []tblutils.StatisticsCollector ) []tblutils.StatisticsCollector {
1674- return results ()
1675- }
1676-
1677- func (a * arrowStatsCollector ) Struct (_ iceberg.StructType , results []func () []tblutils.StatisticsCollector ) []tblutils.StatisticsCollector {
1678- result := make ([]tblutils.StatisticsCollector , 0 , len (results ))
1679- for _ , res := range results {
1680- result = append (result , res ()... )
1675+ func (a * arrowStatsCollector ) resolveColumnMetricsMode (colName string ) tblutils.MetricsMode {
1676+ if a .defaultModeError != nil {
1677+ panic (a .defaultModeError )
16811678 }
16821679
1683- return result
1684- }
1685-
1686- func (a * arrowStatsCollector ) Field (field iceberg.NestedField , fieldRes func () []tblutils.StatisticsCollector ) []tblutils.StatisticsCollector {
1687- a .fieldID = field .ID
1688-
1689- return fieldRes ()
1690- }
1691-
1692- func (a * arrowStatsCollector ) List (list iceberg.ListType , elemResult func () []tblutils.StatisticsCollector ) []tblutils.StatisticsCollector {
1693- a .fieldID = list .ElementID
1694-
1695- return elemResult ()
1696- }
1697-
1698- func (a * arrowStatsCollector ) Map (m iceberg.MapType , keyResult , valResult func () []tblutils.StatisticsCollector ) []tblutils.StatisticsCollector {
1699- a .fieldID = m .KeyID
1700- keyRes := keyResult ()
1701-
1702- a .fieldID = m .ValueID
1703- valRes := valResult ()
1704-
1705- return append (keyRes , valRes ... )
1706- }
1707-
1708- func (a * arrowStatsCollector ) resolveColumnMetricsMode (colName string ) tblutils.MetricsMode {
1709- metMode , err := tblutils .MatchMetricsMode (a .defaultMode )
1710- if err != nil {
1711- panic (err )
1680+ if metMode , ok := a .columnModes [colName ]; ok {
1681+ return metMode
17121682 }
1713- if colMode , ok := a .props [MetricsModeColumnConfPrefix + "." + colName ]; ok {
1714- metMode , err = tblutils .MatchMetricsMode (colMode )
1715- if err != nil {
1716- panic (err )
1717- }
1683+ if err , ok := a .columnModeErrors [colName ]; ok {
1684+ panic (err )
17181685 }
17191686
1720- return metMode
1687+ return a . defaultMode
17211688}
17221689
1723- func (a * arrowStatsCollector ) Primitive (dt iceberg.PrimitiveType ) [] tblutils.StatisticsCollector {
1690+ func (a * arrowStatsCollector ) primitiveCollector (dt iceberg.PrimitiveType , isNested bool ) ( tblutils.StatisticsCollector , bool ) {
17241691 colName , ok := a .schema .FindColumnName (a .fieldID )
17251692 if ! ok {
1726- return [] tblutils.StatisticsCollector {}
1693+ return tblutils.StatisticsCollector {}, false
17271694 }
17281695
17291696 metMode := a .resolveColumnMetricsMode (colName )
@@ -1737,47 +1704,141 @@ func (a *arrowStatsCollector) Primitive(dt iceberg.PrimitiveType) []tblutils.Sta
17371704 }
17381705 }
17391706
1740- isNested := strings .Contains (colName , "." )
17411707 if isNested && (metMode .Typ == tblutils .MetricModeTruncate || metMode .Typ == tblutils .MetricModeFull ) {
17421708 metMode = tblutils.MetricsMode {Typ : tblutils .MetricModeCounts }
17431709 }
17441710
1745- return [] tblutils.StatisticsCollector { {
1711+ return tblutils.StatisticsCollector {
17461712 FieldID : a .fieldID ,
17471713 IcebergTyp : dt ,
17481714 ColName : colName ,
17491715 Mode : metMode ,
1750- }}
1716+ }, true
17511717}
17521718
1753- func (a * arrowStatsCollector ) Variant ( _ iceberg. VariantType ) [] tblutils.StatisticsCollector {
1719+ func (a * arrowStatsCollector ) variantCollector () ( tblutils.StatisticsCollector , bool ) {
17541720 colName , ok := a .schema .FindColumnName (a .fieldID )
17551721 if ! ok {
1756- return [] tblutils.StatisticsCollector {}
1722+ return tblutils.StatisticsCollector {}, false
17571723 }
17581724
1759- return [] tblutils.StatisticsCollector { {
1725+ return tblutils.StatisticsCollector {
17601726 FieldID : a .fieldID ,
17611727 ColName : colName ,
17621728 Mode : a .resolveColumnMetricsMode (colName ),
1763- }}
1729+ }, true
1730+ }
1731+
1732+ func statsPlanFieldCount (field iceberg.NestedField ) int {
1733+ switch typ := field .Type .(type ) {
1734+ case * iceberg.StructType :
1735+ count := 0
1736+ for _ , nestedField := range typ .FieldList {
1737+ count += statsPlanFieldCount (nestedField )
1738+ }
1739+
1740+ return count
1741+ case * iceberg.ListType :
1742+ return statsPlanFieldCount (typ .ElementField ())
1743+ case * iceberg.MapType :
1744+ return statsPlanFieldCount (typ .KeyField ()) + statsPlanFieldCount (typ .ValueField ())
1745+ default :
1746+ return 1
1747+ }
1748+ }
1749+
1750+ func collectStatsPlanField (visitor * arrowStatsCollector , result map [int ]tblutils.StatisticsCollector , field iceberg.NestedField , isNested bool ) {
1751+ switch typ := field .Type .(type ) {
1752+ case * iceberg.StructType :
1753+ for _ , nestedField := range typ .FieldList {
1754+ collectStatsPlanField (visitor , result , nestedField , true )
1755+ }
1756+ case * iceberg.ListType :
1757+ collectStatsPlanField (visitor , result , typ .ElementField (), true )
1758+ case * iceberg.MapType :
1759+ collectStatsPlanField (visitor , result , typ .KeyField (), true )
1760+ collectStatsPlanField (visitor , result , typ .ValueField (), true )
1761+ case iceberg.VariantType :
1762+ visitor .fieldID = field .ID
1763+ if collector , ok := visitor .variantCollector (); ok {
1764+ result [collector .FieldID ] = collector
1765+ }
1766+ default :
1767+ visitor .fieldID = field .ID
1768+ collector , ok := visitor .primitiveCollector (field .Type .(iceberg.PrimitiveType ), isNested )
1769+ if ok {
1770+ result [collector .FieldID ] = collector
1771+ }
1772+ }
17641773}
17651774
1766- func computeStatsPlan (sc * iceberg.Schema , props iceberg.Properties ) (map [int ]tblutils.StatisticsCollector , error ) {
1767- result := make (map [int ]tblutils.StatisticsCollector )
1775+ func computeStatsPlan (sc * iceberg.Schema , props iceberg.Properties ) (result map [int ]tblutils.StatisticsCollector , err error ) {
1776+ defer func () {
1777+ if r := recover (); r != nil {
1778+ result = nil
1779+ switch e := r .(type ) {
1780+ case string :
1781+ err = fmt .Errorf ("%w: %s" , iceberg .ErrInvalidSchema , e )
1782+ case error :
1783+ err = fmt .Errorf ("error encountered during schema visitor: %w" , e )
1784+ }
1785+ }
1786+ }()
1787+
1788+ if sc == nil {
1789+ return nil , fmt .Errorf ("%w: cannot visit nil schema" , iceberg .ErrInvalidArgument )
1790+ }
1791+
1792+ defaultMode , defaultModeErr := tblutils .MatchMetricsMode (
1793+ props .Get (DefaultWriteMetricsModeKey , DefaultWriteMetricsModeDefault ))
1794+ overrideCount := 0
1795+ for key := range props {
1796+ if strings .HasPrefix (key , MetricsModeColumnConfPrefix + "." ) {
1797+ overrideCount ++
1798+ }
1799+ }
1800+
1801+ var columnModes map [string ]tblutils.MetricsMode
1802+ var columnModeErrors map [string ]error
1803+ for key , rawMode := range props {
1804+ colName , ok := strings .CutPrefix (key , MetricsModeColumnConfPrefix + "." )
1805+ if ! ok {
1806+ continue
1807+ }
1808+
1809+ mode , err := tblutils .MatchMetricsMode (rawMode )
1810+ if err != nil {
1811+ if columnModeErrors == nil {
1812+ columnModeErrors = make (map [string ]error , overrideCount )
1813+ }
1814+
1815+ columnModeErrors [colName ] = err
1816+
1817+ continue
1818+ }
1819+ if columnModes == nil {
1820+ columnModes = make (map [string ]tblutils.MetricsMode , overrideCount )
1821+ }
1822+ columnModes [colName ] = mode
1823+ }
17681824
17691825 visitor := & arrowStatsCollector {
1770- schema : sc , props : props ,
1771- defaultMode : props .Get (DefaultWriteMetricsModeKey , DefaultWriteMetricsModeDefault ),
1826+ schema : sc ,
1827+ defaultMode : defaultMode ,
1828+ defaultModeError : defaultModeErr ,
1829+ columnModes : columnModes ,
1830+ columnModeErrors : columnModeErrors ,
17721831 }
17731832
1774- collectors , err := iceberg .PreOrderVisit (sc , visitor )
1775- if err != nil {
1776- return nil , err
1833+ fields := sc .FieldsRef (internal.SchemaRef {})
1834+ resultCount := 0
1835+ for _ , field := range fields {
1836+ resultCount += statsPlanFieldCount (field )
17771837 }
17781838
1779- for _ , entry := range collectors {
1780- result [entry .FieldID ] = entry
1839+ result = make (map [int ]tblutils.StatisticsCollector , resultCount )
1840+ for _ , field := range fields {
1841+ collectStatsPlanField (visitor , result , field , strings .Contains (field .Name , "." ))
17811842 }
17821843
17831844 return result , nil
0 commit comments