@@ -152,4 +152,174 @@ TEST(PartitionSpecTest, PartitionTypeTest) {
152152 EXPECT_EQ (pt_field3, partition_type.value ()->fields ()[2 ]);
153153}
154154
155+ TEST (PartitionSpecTest, MissingSourceColumn) {
156+ SchemaField field1 (1 , " id" , int64 (), false );
157+ SchemaField field2 (2 , " ts" , timestamp (), false );
158+ SchemaField field3 (6 , " s" , string (), false );
159+ Schema schema ({field1, field2, field3}, Schema::kInitialSchemaId );
160+
161+ // Try to create partition field with non-existent source column ID 999
162+ PartitionField pt_field_invalid (999 , 1000 , " missing_partition" , Transform::Identity ());
163+
164+ auto result = PartitionSpec::Make (schema, 1 , {pt_field_invalid});
165+ EXPECT_FALSE (result.has_value ());
166+ EXPECT_THAT (result.error ().message ,
167+ ::testing::HasSubstr (" Cannot find source column for partition field" ));
168+ }
169+
170+ TEST (PartitionSpecTest, InvalidTransformForType) {
171+ // Test Day transform on string type (should fail)
172+ SchemaField field_string (6 , " s" , string (), false );
173+ Schema schema_string ({field_string}, Schema::kInitialSchemaId );
174+
175+ PartitionField pt_field_invalid (6 , 1005 , " s_day" , Transform::Day ());
176+
177+ auto result_string = PartitionSpec::Make (schema_string, 1 , {pt_field_invalid});
178+ EXPECT_FALSE (result_string.has_value ());
179+ EXPECT_THAT (result_string.error ().message , ::testing::HasSubstr (" Invalid source type" ));
180+ }
181+
182+ TEST (PartitionSpecTest, SourceIdNotFound) {
183+ SchemaField field1 (1 , " id" , int64 (), false );
184+ SchemaField field2 (2 , " ts" , timestamp (), false );
185+ Schema schema ({field1, field2}, Schema::kInitialSchemaId );
186+
187+ // Try to create partition field with source ID 99 which doesn't exist
188+ PartitionField pt_field_invalid (99 , 1000 , " Test" , Transform::Identity ());
189+
190+ auto result = PartitionSpec::Make (schema, 1 , {pt_field_invalid});
191+ EXPECT_FALSE (result.has_value ());
192+ EXPECT_THAT (result.error ().message ,
193+ ::testing::HasSubstr (" Cannot find source column for partition field" ));
194+ }
195+
196+ TEST (PartitionSpecTest, PartitionFieldInStruct) {
197+ SchemaField field1 (1 , " id" , int64 (), false );
198+ SchemaField field2 (2 , " ts" , timestamp (), false );
199+ Schema base_schema ({field1, field2}, Schema::kInitialSchemaId );
200+
201+ // Create a struct that contains the base schema fields
202+ auto struct_type =
203+ std::make_shared<StructType>(std::vector<SchemaField>{field1, field2});
204+ SchemaField outer_struct (11 , " MyStruct" , struct_type, false );
205+
206+ Schema schema ({outer_struct}, Schema::kInitialSchemaId );
207+
208+ // Partition on a field within the struct (id field with ID 1)
209+ PartitionField pt_field (1 , 1000 , " id_partition" , Transform::Identity ());
210+
211+ auto result = PartitionSpec::Make (schema, 1 , {pt_field});
212+ EXPECT_TRUE (result.has_value ()) << result.error ().message ;
213+ }
214+
215+ TEST (PartitionSpecTest, PartitionFieldInStructInStruct) {
216+ SchemaField field1 (1 , " id" , int64 (), false );
217+ SchemaField field2 (2 , " ts" , timestamp (), false );
218+
219+ // Create inner struct
220+ auto inner_struct =
221+ std::make_shared<StructType>(std::vector<SchemaField>{field1, field2});
222+ SchemaField inner_field (11 , " Inner" , inner_struct, false );
223+
224+ // Create outer struct containing inner struct
225+ auto outer_struct = std::make_shared<StructType>(std::vector<SchemaField>{inner_field});
226+ SchemaField outer_field (12 , " Outer" , outer_struct, true );
227+
228+ Schema schema ({outer_field}, Schema::kInitialSchemaId );
229+
230+ // Partition on a field deep within nested structs (id field with ID 1)
231+ PartitionField pt_field (1 , 1000 , " id_partition" , Transform::Identity ());
232+
233+ auto result = PartitionSpec::Make (schema, 1 , {pt_field});
234+ EXPECT_TRUE (result.has_value ()) << result.error ().message ;
235+ }
236+
237+ TEST (PartitionSpecTest, PartitionFieldInList) {
238+ auto list_type = std::make_shared<ListType>(1 , int32 (), /* element_required=*/ false );
239+ SchemaField list_field (2 , " MyList" , list_type, false );
240+
241+ Schema schema ({list_field}, Schema::kInitialSchemaId );
242+
243+ // Try to partition on the list element field (field ID 1 is the element)
244+ PartitionField pt_field (1 , 1000 , " element_partition" , Transform::Identity ());
245+
246+ auto result = PartitionSpec::Make (schema, 1 , {pt_field});
247+ EXPECT_FALSE (result.has_value ());
248+ EXPECT_THAT (result.error ().message ,
249+ ::testing::HasSubstr (" Invalid partition field parent" ));
250+ }
251+
252+ TEST (PartitionSpecTest, PartitionFieldInStructInList) {
253+ auto struct_in_list = std::make_shared<StructType>(
254+ std::vector<SchemaField>{SchemaField (1 , " Foo" , int32 (), true )});
255+ auto list_type = std::make_shared<ListType>(2 , struct_in_list,
256+ /* element_required=*/ false );
257+ SchemaField list_field (3 , " MyList" , list_type, false );
258+
259+ Schema schema ({list_field}, Schema::kInitialSchemaId );
260+
261+ // Try to partition on a field within a struct inside a list (Foo field with ID 1)
262+ PartitionField pt_field (1 , 1000 , " foo_partition" , Transform::Identity ());
263+
264+ auto result = PartitionSpec::Make (schema, 1 , {pt_field});
265+ EXPECT_FALSE (result.has_value ());
266+ EXPECT_THAT (result.error ().message ,
267+ ::testing::HasSubstr (" Invalid partition field parent" ));
268+ }
269+
270+ TEST (PartitionSpecTest, PartitionFieldInMap) {
271+ SchemaField key_field (1 , " key" , int32 (), false );
272+ SchemaField value_field (2 , " value" , int32 (), false );
273+ auto map_type = std::make_shared<MapType>(key_field, value_field);
274+ SchemaField map_field (3 , " MyMap" , map_type, false );
275+
276+ Schema schema ({map_field}, Schema::kInitialSchemaId );
277+
278+ // Try to partition on the map key field (field ID 1 is the key)
279+ PartitionField pt_field_key (1 , 1000 , " key_partition" , Transform::Identity ());
280+
281+ auto result_key = PartitionSpec::Make (schema, 1 , {pt_field_key});
282+ EXPECT_FALSE (result_key.has_value ());
283+ EXPECT_THAT (result_key.error ().message ,
284+ ::testing::HasSubstr (" Invalid partition field parent" ));
285+
286+ // Try to partition on the map value field (field ID 2 is the value)
287+ PartitionField pt_field_value (2 , 1001 , " value_partition" , Transform::Identity ());
288+
289+ auto result_value = PartitionSpec::Make (schema, 1 , {pt_field_value});
290+ EXPECT_FALSE (result_value.has_value ());
291+ EXPECT_THAT (result_value.error ().message ,
292+ ::testing::HasSubstr (" Invalid partition field parent" ));
293+ }
294+
295+ TEST (PartitionSpecTest, PartitionFieldInStructInMap) {
296+ auto struct_key = std::make_shared<StructType>(
297+ std::vector<SchemaField>{SchemaField (1 , " Foo" , int32 (), true )});
298+ auto struct_value = std::make_shared<StructType>(
299+ std::vector<SchemaField>{SchemaField (2 , " Bar" , int32 (), true )});
300+
301+ SchemaField key_field (3 , " key" , struct_key, false );
302+ SchemaField value_field (4 , " value" , struct_value, false );
303+ auto map_type = std::make_shared<MapType>(key_field, value_field);
304+ SchemaField map_field (5 , " MyMap" , map_type, false );
305+
306+ Schema schema ({map_field}, Schema::kInitialSchemaId );
307+
308+ // Try to partition on a field within the key struct (Foo field with ID 1)
309+ PartitionField pt_field_key (1 , 1000 , " foo_partition" , Transform::Identity ());
310+
311+ auto result_key = PartitionSpec::Make (schema, 1 , {pt_field_key});
312+ EXPECT_FALSE (result_key.has_value ());
313+ EXPECT_THAT (result_key.error ().message ,
314+ ::testing::HasSubstr (" Invalid partition field parent" ));
315+
316+ // Try to partition on a field within the value struct (Bar field with ID 2)
317+ PartitionField pt_field_value (2 , 1001 , " bar_partition" , Transform::Identity ());
318+
319+ auto result_value = PartitionSpec::Make (schema, 1 , {pt_field_value});
320+ EXPECT_FALSE (result_value.has_value ());
321+ EXPECT_THAT (result_value.error ().message ,
322+ ::testing::HasSubstr (" Invalid partition field parent" ));
323+ }
324+
155325} // namespace iceberg
0 commit comments