3030import io .delta .kernel .data .MapValue ;
3131import io .delta .kernel .defaults .engine .DefaultEngine ;
3232import io .delta .kernel .engine .Engine ;
33+ import io .delta .kernel .engine .FileReadResult ;
3334import io .delta .kernel .internal .InternalScanFileUtils ;
3435import io .delta .kernel .internal .data .ScanStateRow ;
3536import io .delta .kernel .types .ArrayType ;
5253import io .delta .kernel .utils .CloseableIterator ;
5354import io .delta .kernel .utils .FileStatus ;
5455import java .io .Serializable ;
55- import java .math .BigDecimal ;
5656import java .util .ArrayList ;
5757import java .util .HashMap ;
5858import java .util .List ;
6767import org .apache .beam .sdk .values .PBegin ;
6868import org .apache .beam .sdk .values .PCollection ;
6969import org .apache .beam .sdk .values .Row ;
70- import org .apache .hadoop .conf .Configuration ;
7170import org .checkerframework .checker .nullness .qual .Nullable ;
7271
7372/**
@@ -127,25 +126,29 @@ public ReadRows withConfig(Map<String, String> config) {
127126
128127 @ Override
129128 public PCollection <Row > expand (PBegin input ) {
130- if (getTablePath () == null ) {
129+ String tablePath = getTablePath ();
130+ if (tablePath == null ) {
131131 throw new IllegalArgumentException ("Table path must be set." );
132132 }
133133
134134 org .apache .hadoop .conf .Configuration hadoopConfig = getHadoopConfiguration (getHadoopConfig ());
135135 Engine engine = DefaultEngine .create (hadoopConfig );
136- Table table = Table .forPath (engine , getTablePath () );
136+ Table table = Table .forPath (engine , tablePath );
137137
138138 try {
139139 Snapshot snapshot = buildSnapshot (engine , table );
140- Scan scan = snapshot .getScanBuilder (engine ).build ();
141- StructType readSchema = scan .getSchema (engine );
140+ Scan scan = snapshot .getScanBuilder ().build ();
141+ io .delta .kernel .data .Row scanState = scan .getScanState (engine );
142+ StructType readSchema = ScanStateRow .getLogicalSchema (scanState );
142143 org .apache .beam .sdk .schemas .Schema beamSchema = inferBeamSchema (readSchema );
143144
144- List <DeltaFileDescriptor > fileDescriptors = buildFileDescriptors (engine , scan );
145+ List <DeltaFileDescriptor > fileDescriptors = buildFileDescriptors (engine , scan , tablePath );
145146
146147 return input
147- .apply ("CreateFileDescriptors" , Create .of (fileDescriptors )
148- .withCoder (SerializableCoder .of (DeltaFileDescriptor .class )))
148+ .apply (
149+ "CreateFileDescriptors" ,
150+ Create .of (fileDescriptors )
151+ .withCoder (SerializableCoder .of (DeltaFileDescriptor .class )))
149152 .apply ("ReadFile" , ParDo .of (new ReadFileFn (beamSchema )))
150153 .setRowSchema (beamSchema );
151154
@@ -155,34 +158,38 @@ public PCollection<Row> expand(PBegin input) {
155158 }
156159
157160 private Snapshot buildSnapshot (Engine engine , Table table ) throws Exception {
158- if (getVersion () != null ) {
159- return table .getSnapshotAsOfVersion (engine , getVersion ());
160- } else if (getTimestamp () != null ) {
161- long epochMillis = org .joda .time .Instant .parse (getTimestamp ()).getMillis ();
161+ Long version = getVersion ();
162+ String timestamp = getTimestamp ();
163+ if (version != null ) {
164+ return table .getSnapshotAsOfVersion (engine , version );
165+ } else if (timestamp != null ) {
166+ long epochMillis = org .joda .time .Instant .parse (timestamp ).getMillis ();
162167 return table .getSnapshotAsOfTimestamp (engine , epochMillis );
163168 } else {
164169 return table .getLatestSnapshot (engine );
165170 }
166171 }
167172
168- private List <DeltaFileDescriptor > buildFileDescriptors (Engine engine , Scan scan ) throws Exception {
173+ private List <DeltaFileDescriptor > buildFileDescriptors (
174+ Engine engine , Scan scan , String tablePath ) throws Exception {
169175 List <DeltaFileDescriptor > descriptors = new ArrayList <>();
170176 try (CloseableIterator <FilteredColumnarBatch > scanFileIter = scan .getScanFiles (engine )) {
171177 while (scanFileIter .hasNext ()) {
172178 FilteredColumnarBatch scanFilesBatch = scanFileIter .next ();
173- try (CloseableIterator <io .delta .kernel .data .Row > scanFileRows = scanFilesBatch .getRows ()) {
179+ try (CloseableIterator <io .delta .kernel .data .Row > scanFileRows =
180+ scanFilesBatch .getRows ()) {
174181 while (scanFileRows .hasNext ()) {
175182 io .delta .kernel .data .Row scanFileRow = scanFileRows .next ();
176183 FileStatus fileStatus = InternalScanFileUtils .getAddFileStatus (scanFileRow );
177- descriptors .add (new DeltaFileDescriptor (
178- getTablePath (),
179- fileStatus . getPath () ,
180- fileStatus .getSize (),
181- fileStatus .getModificationTime (),
182- getHadoopConfig (),
183- getVersion (),
184- getTimestamp ()
185- ));
184+ descriptors .add (
185+ new DeltaFileDescriptor (
186+ tablePath ,
187+ fileStatus .getPath (),
188+ fileStatus .getSize (),
189+ fileStatus . getModificationTime (),
190+ getHadoopConfig (),
191+ getVersion (),
192+ getTimestamp () ));
186193 }
187194 }
188195 }
@@ -256,47 +263,50 @@ public ReadFileFn(org.apache.beam.sdk.schemas.Schema beamSchema) {
256263 @ ProcessElement
257264 public void processElement (ProcessContext c ) throws Exception {
258265 DeltaFileDescriptor desc = c .element ();
259- org .apache .hadoop .conf .Configuration hadoopConfig = getHadoopConfiguration (desc .getHadoopConfig ());
266+ org .apache .hadoop .conf .Configuration hadoopConfig =
267+ getHadoopConfiguration (desc .getHadoopConfig ());
260268 Engine engine = DefaultEngine .create (hadoopConfig );
261269 Table table = Table .forPath (engine , desc .getTablePath ());
262270
263271 Snapshot snapshot ;
264- if (desc .getVersion () != null ) {
265- snapshot = table .getSnapshotAsOfVersion (engine , desc .getVersion ());
266- } else if (desc .getTimestamp () != null ) {
267- long epochMillis = org .joda .time .Instant .parse (desc .getTimestamp ()).getMillis ();
272+ Long version = desc .getVersion ();
273+ String timestamp = desc .getTimestamp ();
274+ if (version != null ) {
275+ snapshot = table .getSnapshotAsOfVersion (engine , version );
276+ } else if (timestamp != null ) {
277+ long epochMillis = org .joda .time .Instant .parse (timestamp ).getMillis ();
268278 snapshot = table .getSnapshotAsOfTimestamp (engine , epochMillis );
269279 } else {
270280 snapshot = table .getLatestSnapshot (engine );
271281 }
272282
273- Scan scan = snapshot .getScanBuilder (engine ).build ();
283+ Scan scan = snapshot .getScanBuilder ().build ();
274284 io .delta .kernel .data .Row scanState = scan .getScanState (engine );
275285
276286 try (CloseableIterator <FilteredColumnarBatch > scanFileIter = scan .getScanFiles (engine )) {
277287 while (scanFileIter .hasNext ()) {
278288 FilteredColumnarBatch scanFilesBatch = scanFileIter .next ();
279- try (CloseableIterator <io .delta .kernel .data .Row > scanFileRows = scanFilesBatch .getRows ()) {
289+ try (CloseableIterator <io .delta .kernel .data .Row > scanFileRows =
290+ scanFilesBatch .getRows ()) {
280291 while (scanFileRows .hasNext ()) {
281292 io .delta .kernel .data .Row scanFileRow = scanFileRows .next ();
282293 FileStatus fileStatus = InternalScanFileUtils .getAddFileStatus (scanFileRow );
283294 if (fileStatus .getPath ().equals (desc .getFilePath ())) {
284295 StructType physicalReadSchema = ScanStateRow .getPhysicalDataReadSchema (scanState );
285296 CloseableIterator <ColumnarBatch > physicalDataIter =
286- engine .getParquetHandler ().readParquetFiles (
287- singletonCloseableIterator (fileStatus ),
288- physicalReadSchema ,
289- Optional .empty ()).map (FilteredColumnarBatch ::getData );
290- try (
291- CloseableIterator <FilteredColumnarBatch > transformedData =
292- Scan .transformPhysicalData (
293- engine ,
294- scanState ,
295- scanFileRow ,
296- physicalDataIter )) {
297+ engine
298+ .getParquetHandler ()
299+ .readParquetFiles (
300+ singletonCloseableIterator (fileStatus ),
301+ physicalReadSchema ,
302+ Optional .empty ())
303+ .map (FileReadResult ::getData );
304+ try (CloseableIterator <FilteredColumnarBatch > transformedData =
305+ Scan .transformPhysicalData (engine , scanState , scanFileRow , physicalDataIter )) {
297306 while (transformedData .hasNext ()) {
298307 FilteredColumnarBatch filteredData = transformedData .next ();
299- try (CloseableIterator <io .delta .kernel .data .Row > rows = filteredData .getRows ()) {
308+ try (CloseableIterator <io .delta .kernel .data .Row > rows =
309+ filteredData .getRows ()) {
300310 while (rows .hasNext ()) {
301311 io .delta .kernel .data .Row row = rows .next ();
302312 c .output (convertKernelRowToBeamRow (row , beamSchema ));
@@ -323,8 +333,9 @@ private static org.apache.hadoop.conf.Configuration getHadoopConfiguration(
323333 return config ;
324334 }
325335
326- private static org .apache .beam .sdk .schemas .Schema inferBeamSchema (StructType structType ) {
327- org .apache .beam .sdk .schemas .Schema .Builder builder = org .apache .beam .sdk .schemas .Schema .builder ();
336+ static org .apache .beam .sdk .schemas .Schema inferBeamSchema (StructType structType ) {
337+ org .apache .beam .sdk .schemas .Schema .Builder builder =
338+ org .apache .beam .sdk .schemas .Schema .builder ();
328339 for (StructField field : structType .fields ()) {
329340 builder .addField (field .getName (), toBeamFieldType (field .getDataType ()));
330341 }
@@ -355,22 +366,23 @@ private static org.apache.beam.sdk.schemas.Schema.FieldType toBeamFieldType(Data
355366 } else if (dataType instanceof TimestampType || dataType instanceof DateType ) {
356367 return org .apache .beam .sdk .schemas .Schema .FieldType .DATETIME ;
357368 } else if (dataType instanceof StructType ) {
358- return org .apache .beam .sdk .schemas .Schema .FieldType .row (inferBeamSchema ((StructType ) dataType ));
369+ return org .apache .beam .sdk .schemas .Schema .FieldType .row (
370+ inferBeamSchema ((StructType ) dataType ));
359371 } else if (dataType instanceof ArrayType ) {
360- return org .apache .beam .sdk .schemas .Schema .FieldType .array (toBeamFieldType (((ArrayType ) dataType ).getElementType ()));
372+ return org .apache .beam .sdk .schemas .Schema .FieldType .array (
373+ toBeamFieldType (((ArrayType ) dataType ).getElementType ()));
361374 } else if (dataType instanceof MapType ) {
362375 MapType mapType = (MapType ) dataType ;
363376 return org .apache .beam .sdk .schemas .Schema .FieldType .map (
364- toBeamFieldType (mapType .getKeyType ()),
365- toBeamFieldType (mapType .getValueType ()));
377+ toBeamFieldType (mapType .getKeyType ()), toBeamFieldType (mapType .getValueType ()));
366378 } else {
367379 throw new IllegalArgumentException ("Unsupported Delta type: " + dataType );
368380 }
369381 }
370382
371383 private static Row convertKernelRowToBeamRow (
372384 io .delta .kernel .data .Row deltaRow , org .apache .beam .sdk .schemas .Schema beamSchema ) {
373- List <Object > values = new ArrayList <>();
385+ List <@ Nullable Object > values = new ArrayList <>();
374386 StructType structType = deltaRow .getSchema ();
375387 for (int i = 0 ; i < structType .length (); i ++) {
376388 if (deltaRow .isNullAt (i )) {
@@ -383,7 +395,8 @@ private static Row convertKernelRowToBeamRow(
383395 return Row .withSchema (beamSchema ).addValues (values ).build ();
384396 }
385397
386- private static Object convertValue (io .delta .kernel .data .Row deltaRow , int ordinal , DataType dataType ) {
398+ private static @ Nullable Object convertValue (
399+ io .delta .kernel .data .Row deltaRow , int ordinal , DataType dataType ) {
387400 if (deltaRow .isNullAt (ordinal )) {
388401 return null ;
389402 }
@@ -419,7 +432,7 @@ private static Object convertValue(io.delta.kernel.data.Row deltaRow, int ordina
419432 } else if (dataType instanceof ArrayType ) {
420433 ArrayValue arrayVal = deltaRow .getArray (ordinal );
421434 int size = arrayVal .getSize ();
422- List <Object > list = new ArrayList <>(size );
435+ List <@ Nullable Object > list = new ArrayList <>(size );
423436 DataType elemType = ((ArrayType ) dataType ).getElementType ();
424437 ColumnVector vec = arrayVal .getElements ();
425438 for (int j = 0 ; j < size ; j ++) {
@@ -433,7 +446,7 @@ private static Object convertValue(io.delta.kernel.data.Row deltaRow, int ordina
433446 } else if (dataType instanceof MapType ) {
434447 MapValue mapVal = deltaRow .getMap (ordinal );
435448 int size = mapVal .getSize ();
436- Map <Object , Object > map = new HashMap <>(size );
449+ Map <Object , @ Nullable Object > map = new HashMap <>(size );
437450 DataType keyType = ((MapType ) dataType ).getKeyType ();
438451 DataType valueType = ((MapType ) dataType ).getValueType ();
439452 ColumnVector keysVec = mapVal .getKeys ();
0 commit comments