@@ -721,3 +721,139 @@ from t1
721721where struct_col['field'][1]['nested'] > 1;
722722----
7237232
724+
725+ ###
726+ # Comprehensive struct column filter pushdown tests
727+ # Tests for various struct access patterns being pushed down to parquet
728+ ###
729+
730+ statement ok
731+ COPY (
732+ SELECT * FROM VALUES
733+ ({id: 1, name: 'alice', score: 75.0, status: 'active', optional_field: arrow_cast(10, 'Int64')},
734+ [{item_id: 1, value: 100.0}, {item_id: 2, value: 200.0}],
735+ {outer: {inner: 1}}),
736+ ({id: 2, name: 'bob', score: 45.0, status: 'inactive', optional_field: arrow_cast(NULL, 'Int64')},
737+ [{item_id: 3, value: 300.0}, {item_id: 4, value: 400.0}],
738+ {outer: {inner: 2}}),
739+ ({id: 3, name: 'charlie', score: 90.0, status: 'active', optional_field: arrow_cast(30, 'Int64')},
740+ [{item_id: 5, value: 500.0}],
741+ {outer: {inner: 3}})
742+ AS t(struct_col, list_of_structs, nested_struct)
743+ ) TO 'test_files/scratch/parquet_filter_pushdown/struct_tests.parquet';
744+
745+ statement ok
746+ DROP TABLE IF EXISTS struct_test;
747+
748+ statement ok
749+ CREATE EXTERNAL TABLE struct_test
750+ STORED AS PARQUET
751+ LOCATION 'test_files/scratch/parquet_filter_pushdown/struct_tests.parquet';
752+
753+ # Category 1: Simple struct field access
754+
755+ # Test equality filter on struct field - verify pushdown in EXPLAIN
756+ query TT
757+ EXPLAIN SELECT struct_col FROM struct_test WHERE struct_col['id'] = 1;
758+ ----
759+ logical_plan
760+ 01)Filter: get_field(struct_test.struct_col, Utf8("id")) = Int64(1)
761+ 02)--TableScan: struct_test projection=[struct_col], partial_filters=[get_field(struct_test.struct_col, Utf8("id")) = Int64(1)]
762+ physical_plan DataSourceExec: file_groups={1 group: [[WORKSPACE_ROOT/datafusion/sqllogictest/test_files/scratch/parquet_filter_pushdown/struct_tests.parquet]]}, projection=[struct_col], file_type=parquet, predicate=get_field(struct_col@0, id) = 1
763+
764+ query ?
765+ SELECT struct_col FROM struct_test WHERE struct_col['id'] = 1;
766+ ----
767+ {id: 1, name: alice, score: 75.0, status: active, optional_field: 10}
768+
769+ # Test comparison filter on struct field
770+ query ?
771+ SELECT struct_col FROM struct_test WHERE struct_col['score'] > 50.0 ORDER BY struct_col['id'];
772+ ----
773+ {id: 1, name: alice, score: 75.0, status: active, optional_field: 10}
774+ {id: 3, name: charlie, score: 90.0, status: active, optional_field: 30}
775+
776+ # Test string equality filter on struct field
777+ query ?
778+ SELECT struct_col FROM struct_test WHERE struct_col['status'] = 'active' ORDER BY struct_col['id'];
779+ ----
780+ {id: 1, name: alice, score: 75.0, status: active, optional_field: 10}
781+ {id: 3, name: charlie, score: 90.0, status: active, optional_field: 30}
782+
783+ # Category 2: List of structs access
784+
785+ # Test list element struct field access - verify pushdown in EXPLAIN
786+ query TT
787+ EXPLAIN SELECT list_of_structs FROM struct_test WHERE list_of_structs[1]['item_id'] = 1;
788+ ----
789+ logical_plan
790+ 01)Filter: get_field(array_element(struct_test.list_of_structs, Int64(1)), Utf8("item_id")) = Int64(1)
791+ 02)--TableScan: struct_test projection=[list_of_structs], partial_filters=[get_field(array_element(struct_test.list_of_structs, Int64(1)), Utf8("item_id")) = Int64(1)]
792+ physical_plan DataSourceExec: file_groups={1 group: [[WORKSPACE_ROOT/datafusion/sqllogictest/test_files/scratch/parquet_filter_pushdown/struct_tests.parquet]]}, projection=[list_of_structs], file_type=parquet, predicate=get_field(array_element(list_of_structs@1, 1), item_id) = 1
793+
794+ query ?
795+ SELECT list_of_structs FROM struct_test WHERE list_of_structs[1]['item_id'] = 1;
796+ ----
797+ [{item_id: 1, value: 100.0}, {item_id: 2, value: 200.0}]
798+
799+ # Test list element struct field with comparison
800+ query ?
801+ SELECT list_of_structs FROM struct_test WHERE list_of_structs[1]['value'] > 200.0 ORDER BY struct_col['id'];
802+ ----
803+ [{item_id: 3, value: 300.0}, {item_id: 4, value: 400.0}]
804+ [{item_id: 5, value: 500.0}]
805+
806+ # Category 3: Nested struct access
807+
808+ # Test nested struct field access - verify pushdown in EXPLAIN
809+ query TT
810+ EXPLAIN SELECT nested_struct FROM struct_test WHERE nested_struct['outer']['inner'] = 2;
811+ ----
812+ logical_plan
813+ 01)Filter: get_field(struct_test.nested_struct, Utf8("outer"), Utf8("inner")) = Int64(2)
814+ 02)--TableScan: struct_test projection=[nested_struct], partial_filters=[get_field(struct_test.nested_struct, Utf8("outer"), Utf8("inner")) = Int64(2)]
815+ physical_plan DataSourceExec: file_groups={1 group: [[WORKSPACE_ROOT/datafusion/sqllogictest/test_files/scratch/parquet_filter_pushdown/struct_tests.parquet]]}, projection=[nested_struct], file_type=parquet, predicate=get_field(nested_struct@2, outer, inner) = 2
816+
817+ query ?
818+ SELECT nested_struct FROM struct_test WHERE nested_struct['outer']['inner'] = 2;
819+ ----
820+ {outer: {inner: 2}}
821+
822+ # Category 4: Combined predicates with AND/OR
823+
824+ # Test AND with multiple struct field predicates
825+ query ?
826+ SELECT struct_col FROM struct_test
827+ WHERE struct_col['status'] = 'active' AND struct_col['score'] > 80.0;
828+ ----
829+ {id: 3, name: charlie, score: 90.0, status: active, optional_field: 30}
830+
831+ # Test OR with struct field predicates
832+ query ?
833+ SELECT struct_col FROM struct_test
834+ WHERE struct_col['id'] = 1 OR struct_col['id'] = 3
835+ ORDER BY struct_col['id'];
836+ ----
837+ {id: 1, name: alice, score: 75.0, status: active, optional_field: 10}
838+ {id: 3, name: charlie, score: 90.0, status: active, optional_field: 30}
839+
840+ # Category 5: NULL handling
841+
842+ # Test IS NULL filter on struct field
843+ query ?
844+ SELECT struct_col FROM struct_test WHERE struct_col['optional_field'] IS NULL;
845+ ----
846+ {id: 2, name: bob, score: 45.0, status: inactive, optional_field: NULL}
847+
848+ # Test IS NOT NULL filter on struct field
849+ query ?
850+ SELECT struct_col FROM struct_test
851+ WHERE struct_col['optional_field'] IS NOT NULL
852+ ORDER BY struct_col['id'];
853+ ----
854+ {id: 1, name: alice, score: 75.0, status: active, optional_field: 10}
855+ {id: 3, name: charlie, score: 90.0, status: active, optional_field: 30}
856+
857+ # Cleanup
858+ statement ok
859+ DROP TABLE struct_test;
0 commit comments