shoemoney commented on code in PR #2034:
URL: https://github.com/apache/iceberg-go/pull/2034#discussion_r4064379810


##########
schema_conversions_test.go:
##########
@@ -282,3 +283,76 @@ func TestNonDayInt32PartitionAvroIntEncoding(t *testing.T) 
{
        assert.IsType(t, int32(0), got, "plain Int32 partition must decode as 
int32, not time.Time")
        assert.Equal(t, int32(42), got)
 }
+
+// TestNanosecondPartitionAvroSchema verifies that v3 nanosecond timestamp
+// types are accepted as partition types and round trip through Avro at
+// nanosecond precision. The read path in manifest.go already decodes
+// atype.TimestampNanos, so the write path has to emit it.
+func TestNanosecondPartitionAvroSchema(t *testing.T) {
+       cases := []struct {
+               name string
+               typ  Type
+       }{
+               {"timestamp_ns", TimestampNsType{}},
+               {"timestamptz_ns", TimestampTzNsType{}},
+       }
+
+       for _, tc := range cases {
+               t.Run(tc.name, func(t *testing.T) {
+                       partitionType := &StructType{FieldList: []NestedField{
+                               {ID: 1000, Name: "ts_ns", Type: tc.typ, 
Required: false},
+                       }}
+
+                       avroSchema, err := 
partitionTypeToAvroSchema(partitionType)
+                       require.NoError(t, err)
+                       require.NotNil(t, avroSchema)
+
+                       // A value that is not a whole number of microseconds, 
so a
+                       // micros-based logical type would lose the trailing 
123 ns.
+                       want := time.Date(2024, time.March, 1, 12, 0, 0, 
456789123, time.UTC)
+
+                       encoded, err := 
avroSchema.Encode(map[string]any{"ts_ns": want})
+                       require.NoError(t, err)
+
+                       var decoded map[string]any
+                       _, err = avroSchema.Decode(encoded, &decoded)
+                       require.NoError(t, err)
+
+                       got, ok := decoded["ts_ns"]
+                       require.True(t, ok)
+                       gotTime, isTime := got.(time.Time)
+                       require.True(t, isTime, "nanosecond partition field 
must decode as time.Time, got %T", got)
+                       assert.Equal(t, want.UnixNano(), 
gotTime.UTC().UnixNano(),
+                               "nanosecond partition value must round trip 
without precision loss")
+               })
+       }
+}
+
+// TestNewManifestWriterV3NanosecondPartition verifies that a v3 manifest can
+// be written for a table with an identity partition on a nanosecond timestamp
+// column. Before the nanosecond arms existed in partitionTypeToAvroSchema,
+// this failed with "unsupported partition type: timestamp_ns".
+func TestNewManifestWriterV3NanosecondPartition(t *testing.T) {
+       cases := []struct {
+               name string
+               typ  Type
+       }{
+               {"timestamp_ns", TimestampNsType{}},
+               {"timestamptz_ns", TimestampTzNsType{}},
+       }
+
+       for _, tc := range cases {
+               t.Run(tc.name, func(t *testing.T) {
+                       sc := NewSchema(0,
+                               NestedField{ID: 1, Name: "ts", Type: tc.typ, 
Required: true},
+                       )
+                       spec := NewPartitionSpecID(0,
+                               PartitionField{FieldID: 1000, SourceIDs: 
[]int{1}, Name: "ts_identity", Transform: IdentityTransform{}},
+                       )
+
+                       writer, err := NewManifestWriter(3, io.Discard, spec, 
sc, 1)
+                       require.NoError(t, err)
+                       require.NotNil(t, writer)

Review Comment:
   Fixed in 731e48b. Both nanosecond types now use TimestampNano partition 
statistics. The regression adds positive and negative TimestampNano entries, 
calls ToManifestFile, and checks the exact lower/upper bound bytes for both 
types; it fails at finalization on the previous implementation and passes with 
the fix. I also added both ns arms to the non-nullable schema test helper. Full 
make test passes on both the prior head and this change (Go 1.26.1). Pinned 
golangci-lint v2.12.2 reports only the same pre-existing gofmt finding in 
table/snapshot_producers.go:1766 on both.



##########
internal/avro_schemas.go:
##########
@@ -122,6 +125,8 @@ var (
        TimeSchema           = mustSchema(TimeNode)
        TimestampSchema      = mustSchema(TimestampNode)
        TimestampTzSchema    = mustSchema(TimestampTzNode)
+       TimestampNsSchema    = mustSchema(TimestampNsNode)

Review Comment:
   Removed the unused TimestampNsSchema and TimestampTzNsSchema variables in 
731e48b; the node definitions remain in use by partition schema conversion. 
Both nullable and non-nullable nanosecond schema round-trip tests pass.



##########
internal/avro_schemas.go:
##########
@@ -101,7 +101,10 @@ var (
        TimeNode        = avro.SchemaNode{Type: atype.Long, LogicalType: 
atype.TimeMicros}
        TimestampNode   = avro.SchemaNode{Type: atype.Long, LogicalType: 
atype.TimestampMicros, Props: map[string]any{"adjust-to-utc": false}}
        TimestampTzNode = avro.SchemaNode{Type: atype.Long, LogicalType: 
atype.TimestampMicros, Props: map[string]any{"adjust-to-utc": true}}
-       UUIDNode        = avro.SchemaNode{Type: atype.Fixed, Name: 
"uuid_fixed", Size: 16, LogicalType: atype.UUID}
+
+       TimestampNsNode   = avro.SchemaNode{Type: atype.Long, LogicalType: 
atype.TimestampNanos, Props: map[string]any{"adjust-to-utc": false}}
+       TimestampTzNsNode = avro.SchemaNode{Type: atype.Long, LogicalType: 
atype.TimestampNanos, Props: map[string]any{"adjust-to-utc": true}}
+       UUIDNode          = avro.SchemaNode{Type: atype.Fixed, Name: 
"uuid_fixed", Size: 16, LogicalType: atype.UUID}

Review Comment:
   Separated UUIDNode from the nanosecond node pair in 731e48b and formatted 
the changed files. The ns pair now forms its own group.



-- 
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