zeroshade commented on code in PR #1846:
URL: https://github.com/apache/iceberg-go/pull/1846#discussion_r3808171196


##########
table/internal/variant_shredding.go:
##########
@@ -47,17 +47,6 @@ var decimalPriority = map[variant.Type]int{
        variant.Decimal4: 0, variant.Decimal8: 1, variant.Decimal16: 2,
 }
 
-// tieBreakPriority breaks count ties in most-common-type selection; higher 
wins.
-// Types absent here (Null, Array, Object) resolve to -1.
-var tieBreakPriority = map[variant.Type]int{
-       variant.Bool: 0, variant.Int8: 1, variant.Int16: 2, variant.Int32: 3,
-       variant.Int64: 4, variant.Float: 5, variant.Double: 6, 
variant.Decimal4: 7,
-       variant.Decimal8: 8, variant.Decimal16: 9, variant.Date: 10, 
variant.Time: 11,
-       variant.TimestampMicros: 12, variant.TimestampMicrosNTZ: 13, 
variant.Binary: 14,
-       variant.String: 15, variant.TimestampNanos: 16, 
variant.TimestampNanosNTZ: 17,
-       variant.UUID: 18,
-}
-
 type fieldInfo struct {
        typeCounts          map[variant.Type]int

Review Comment:
   Non-blocking simplification: now that majority selection is gone, nothing 
reads these per-type counts; only the map keys remain relevant. Consider 
storing admission state directly (`admitted`, `hasAdmitted`, `mixed`) and 
merging each observation through a small helper that widens integer/decimal 
pairs and rejects cross-family pairs. That would remove this per-node map 
allocation and most of the `widestInt`/`widestDec`/`otherFamilies` bookkeeping 
below. If retaining post-hoc calculation, at least model this as `observedTypes 
map[variant.Type]struct{}` so the representation matches the new semantics.



##########
table/internal/variant_shredding_test.go:
##########
@@ -268,22 +354,14 @@ func TestInferredTypedScalarsShred(t *testing.T) {
        }
        for _, c := range cases {
                t.Run(c.name, func(t *testing.T) {
-                       mk := func() variant.Value {
-                               var b variant.Builder
-                               require.NoError(t, c.build(&b))
-                               v, err := b.Build()
-                               require.NoError(t, err)
-
-                               return v
-                       }
-                       v := mk()
+                       v := mkBuilt(t, c.build)
                        inner, ok := AnalyzeVariantShredding([]variant.Value{v})
                        require.Truef(t, ok, "%s should infer a shredded type", 
c.name)
 
                        st := extensions.NewShreddedVariantType(inner)
                        bldr := extensions.NewVariantBuilder(mem, st)
                        defer bldr.Release()
-                       bldr.Append(mk())
+                       bldr.Append(mkBuilt(t, c.build))

Review Comment:
   Non-blocking nit: `v` was already built above and `Append` does not require 
a fresh value, so this can simply be `bldr.Append(v)`. That avoids invoking the 
builder callback twice and makes it clearer that the value used for inference 
is the one being round-tripped.



##########
table/internal/variant_shredding_test.go:
##########
@@ -115,22 +127,96 @@ func TestAnalyzeIntegerWidening(t *testing.T) {
                "widening must pick Int64, got %s", f[0].Type)
 }
 
-func TestAnalyzeMixedTypeMajority(t *testing.T) {
-       // "v" is an int in 7 rows, a string in 3 -> majority int wins.
+// TestAnalyzeMixedTypeNotShredded: a field mixing type families (int in 7 
rows,
+// string in 3) is not uniform and must not shred - it is this object's only
+// field, so nothing shreds at all.
+func TestAnalyzeMixedTypeNotShredded(t *testing.T) {
        var sample []variant.Value
        for range 7 {
                sample = append(sample, mkVar(t, map[string]any{"v": bigI64}))
        }
        for range 3 {
                sample = append(sample, mkVar(t, map[string]any{"v": "s"}))
        }
+       _, ok := AnalyzeVariantShredding(sample)
+       assert.False(t, ok, "mixed int+string field must not shred")
+}
+
+// TestAnalyzeCrossFamilyNotShredded: distinct scalar families that do not 
widen
+// into each other are not uniform and must not shred.
+func TestAnalyzeCrossFamilyNotShredded(t *testing.T) {
+       microsTS := func(b *variant.Builder) error {
+               return 
b.AppendTimestamp(arrow.Timestamp(1_700_000_000_000_000), true, true)
+       }
+       nanosTS := func(b *variant.Builder) error {
+               return 
b.AppendTimestamp(arrow.Timestamp(1_700_000_000_000_000), false, true)
+       }
+       dec := func(b *variant.Builder) error { return b.AppendDecimal8(2, 
decimal.Decimal64(12345)) }
+
+       for _, c := range []struct {
+               name   string
+               sample []variant.Value
+       }{
+               {"float+double", mkVars(t, float32(1.5), float64(2.5))},
+               {"timestamp micros+nanos", []variant.Value{mkBuilt(t, 
microsTS), mkBuilt(t, nanosTS)}},
+               {"int+decimal", []variant.Value{mkVar(t, bigI64), mkBuilt(t, 
dec)}},
+       } {
+               t.Run(c.name, func(t *testing.T) {
+                       _, ok := AnalyzeVariantShredding(c.sample)
+                       assert.Falsef(t, ok, "%s must not shred", c.name)
+               })
+       }
+}
+
+// TestAnalyzeMixedSiblingKeepsUniform: a mixed-type field is dropped from the
+// shredded type while its uniform sibling still shreds.
+func TestAnalyzeMixedSiblingKeepsUniform(t *testing.T) {
+       var sample []variant.Value
+       for range 8 {
+               sample = append(sample, mkVar(t, map[string]any{"good": bigI64, 
"bad": bigI64}))
+       }
+       for range 2 {
+               sample = append(sample, mkVar(t, map[string]any{"good": bigI64, 
"bad": "s"}))
+       }

Review Comment:
   Non-blocking simplification: the preceding test already uses a 7/3 split to 
prove that a majority no longer wins, so this sibling-focused test only needs 
two rows: one with `bad` as an integer and one with it as a string. You could 
then compare `dt` directly with `arrow.StructOf` containing only the expected 
`good` field; that removes the loops, unchecked struct assertion, and field 
lookups while making the no-extra-fields assertion stronger.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to