twuebi commented on code in PR #1665:
URL: https://github.com/apache/iceberg-go/pull/1665#discussion_r3796132776


##########
partitions.go:
##########
@@ -190,6 +206,24 @@ type PartitionSpec struct {
        sourceIdToFields map[int][]PartitionField
 }
 
+// UnboundPartitionSpec decodes a partition spec that a client sent in a
+// create-table request, before it has been bound to a schema. Such a spec
+// carries the client's placeholder source IDs rather than schema field IDs, 
and
+// those placeholders start at zero: Spark numbers the columns of a new table
+// from zero, so partitioning by the first column arrives as source-id 0.
+// Binding the embedded spec to a schema resolves the placeholders to field 
IDs.
+//
+// Use PartitionSpec for specs read from table metadata, where source IDs are
+// bound field IDs and must be positive. Catalog implementations that serve the
+// REST create-table request should decode its partition spec into this type.
+type UnboundPartitionSpec struct {

Review Comment:
   The schema argument isn't the target schema, it's the schema the source IDs 
refer to. For an unbound spec that's the request schema, whose field IDs are 
the placeholders. I probed it: source-id 0 against a request schema with fields 
0,1,2 resolves cleanly to ints, no error. So 
`unboundSpec.BindToSchema(requestSchema, ...)` is the correct call and works 
today.
   
   Rust agrees, and so does Java. `UnboundPartitionSpec::bind` 
(`crates/iceberg/src/spec/partition.rs:275`) resolves by source ID, and 
`UnboundPartitionField` (`partition.rs:243-254`) carries `source_id`, 
`field_id`, `name`, `transform`. Java is the same: `UnboundPartitionSpec.bind` 
calls `schema.findType(field.sourceId)` (`UnboundPartitionSpec.java:61`) and 
`builder.add(field.sourceId, ...)` (line 69), and its `UnboundPartitionField` 
holds no source column name either. So the name isn't in the wire format at 
all, `name` there is the partition field name, and a name-based override isn't 
possible without also passing the request schema, which is what `reassignIDs` 
already does at `table/metadata.go:2958`.
   
   The real hazard was quieter than a failed lookup: `AssignFreshSchemaIDs` 
numbers from 1 (`schema.go:1554-1562`) while placeholders start at 0, so 
binding against the reassigned schema resolved every field off-by-one while 
keeping the client's name, and nothing caught it, `addSpecFieldInternal` 
checked only the transform's own parameters and duplicate partition names. Java 
does catch it: `checkAndAddPartitionName` (`PartitionSpec.java:401-427`) 
requires a partition name that is also a schema column to be sourced from that 
column, and `TableMetadata.java:1189` uses that checked bind in the same 
position as our `table/metadata.go:450`. I've now added the same rule in 
`validatePartitionNameAgainstSchema` (`partitions.go:402`), called from 
`addSpecFieldInternal`, which now takes the schema and so covers both 
`BindToSchema` and the create-table path through `reassignIDs`. The shift above 
now reports partition name floats matches schema column with field ID 2, but 
the field is sourced fr
 om 1. The only test that broke in the tree was the one asserting the silent 
misbind, every catalog suite and the metadata replay tests pass untouched and 
`TestPartitionNameMatchingSchemaColumn`(`partitions_test.go:1000`) covers the 
rule directly.



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