This is an automated email from the ASF dual-hosted git repository.

mattcasters pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new cc5c6a1223 Issue #2251 : Fix integer overflow in ParquetInput for 
files larger than 2GB (#7694)
cc5c6a1223 is described below

commit cc5c6a1223f55a13eaad85400037232e673f6671
Author: hma (Atol CD) <[email protected]>
AuthorDate: Thu Jul 30 16:54:47 2026 +0200

    Issue #2251 : Fix integer overflow in ParquetInput for files larger than 
2GB (#7694)
---
 .../hop/parquet/transforms/input/ParquetInput.java |  15 +--
 .../parquet/transforms/input/ParquetInputData.java |   3 +-
 .../parquet/transforms/input/ParquetInputMeta.java |  11 +-
 .../parquet/transforms/input/ParquetStream.java    | 131 +++++++++++++++++----
 .../parquet/transforms/output/ParquetTestUtil.java |  11 +-
 5 files changed, 122 insertions(+), 49 deletions(-)

diff --git 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInput.java
 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInput.java
index 2352ad73fd..d51145e113 100644
--- 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInput.java
+++ 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInput.java
@@ -17,11 +17,9 @@
 
 package org.apache.hop.parquet.transforms.input;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.hop.core.RowMetaAndData;
 import org.apache.hop.core.exception.HopException;
@@ -118,13 +116,8 @@ public class ParquetInput extends 
BaseTransform<ParquetInputMeta, ParquetInputDa
           // optional lineage
         }
       }
-      data.inputStream = HopVfs.getInputStream(fileObject);
 
-      // Reads the whole file into memory...
-      //
-      ByteArrayOutputStream outputStream = new ByteArrayOutputStream((int) 
size);
-      IOUtils.copy(data.inputStream, outputStream);
-      ParquetStream inputFile = new ParquetStream(outputStream.toByteArray(), 
filename);
+      ParquetStream inputFile = new ParquetStream(fileObject, filename);
 
       ParquetReadSupport readSupport = new ParquetReadSupport(fields);
       data.reader = new ParquetReaderBuilder<>(readSupport, inputFile).build();
@@ -146,10 +139,12 @@ public class ParquetInput extends 
BaseTransform<ParquetInputMeta, ParquetInputDa
   }
 
   public void closeFile() {
-    if (!data.readerClosed && data.reader != null && data.inputStream != null) 
{
+    if (!data.readerClosed && data.reader != null) {
       try {
         data.reader.close();
-        data.inputStream.close();
+        if (data.parquetStream != null) {
+          data.parquetStream.close();
+        }
       } catch (IOException e) {
         logError("Unable to properly close parquet reader!");
       }
diff --git 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputData.java
 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputData.java
index 2ebf3aae4f..23736e32ee 100644
--- 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputData.java
+++ 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputData.java
@@ -17,7 +17,6 @@
 
 package org.apache.hop.parquet.transforms.input;
 
-import java.io.InputStream;
 import org.apache.hop.core.RowMetaAndData;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.pipeline.transform.BaseTransformData;
@@ -30,7 +29,7 @@ public class ParquetInputData extends BaseTransformData 
implements ITransformDat
   public IRowMeta outputRowMeta;
   public int filenameFieldIndex;
   public ParquetReader<RowMetaAndData> reader;
-  public InputStream inputStream;
+  public ParquetStream parquetStream;
   public boolean readerClosed = false;
 
   public ParquetInputData() {
diff --git 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputMeta.java
 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputMeta.java
index 1e6c439a40..5d922a0d63 100644
--- 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputMeta.java
+++ 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetInputMeta.java
@@ -17,13 +17,10 @@
 
 package org.apache.hop.parquet.transforms.input;
 
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 import lombok.Getter;
 import lombok.Setter;
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.hop.core.RowMetaAndData;
@@ -120,14 +117,8 @@ public class ParquetInputMeta extends 
BaseTransformMeta<ParquetInput, ParquetInp
     try {
       FileObject fileObject = 
HopVfs.getFileObject(variables.resolve(filename), variables);
 
-      long size = fileObject.getContent().getSize();
-      InputStream inputStream = HopVfs.getInputStream(fileObject);
+      ParquetStream inputFile = new ParquetStream(fileObject, filename);
 
-      // Reads the whole file into memory...
-      //
-      ByteArrayOutputStream outputStream = new ByteArrayOutputStream((int) 
size);
-      IOUtils.copy(inputStream, outputStream);
-      ParquetStream inputFile = new ParquetStream(outputStream.toByteArray(), 
filename);
       // Empty list of fields to retrieve: we still grab the schema
       //
       ParquetReadSupport readSupport = new ParquetReadSupport(new 
ArrayList<>());
diff --git 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetStream.java
 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetStream.java
index 1719bd7676..d5de7cbb12 100644
--- 
a/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetStream.java
+++ 
b/plugins/tech/parquet/src/main/java/org/apache/hop/parquet/transforms/input/ParquetStream.java
@@ -17,39 +17,68 @@
 
 package org.apache.hop.parquet.transforms.input;
 
-import java.io.ByteArrayInputStream;
+import java.io.Closeable;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Paths;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.hop.core.vfs.HopVfs;
 import org.apache.parquet.io.DelegatingSeekableInputStream;
 import org.apache.parquet.io.InputFile;
+import org.apache.parquet.io.LocalInputFile;
 import org.apache.parquet.io.SeekableInputStream;
 
-public class ParquetStream implements InputFile {
-  private final byte[] data;
+public class ParquetStream implements InputFile, Closeable {
+
+  private final FileObject fileObject;
   private final String filename;
+  private final boolean isLocal;
+  private final LocalInputFile localInputFile;
 
-  public ParquetStream(byte[] contents, String filename) {
+  public ParquetStream(FileObject fileObject, String filename) throws 
IOException {
+    this.fileObject = fileObject;
     this.filename = filename;
-    this.data = contents;
+    // Detect if the file is local by checking the VFS scheme
+    // For remote files, localInputFile is null. VfsSeekableInputStream will 
be used instead
+    this.isLocal = "file".equals(fileObject.getName().getScheme());
+    this.localInputFile =
+        isLocal ? new 
LocalInputFile(Paths.get(fileObject.getName().getPath())) : null;
   }
 
   @Override
   public long getLength() throws IOException {
-    return this.data.length;
+    if (isLocal) {
+      return localInputFile.getLength();
+    }
+    return fileObject.getContent().getSize();
   }
 
   @Override
   public SeekableInputStream newStream() throws IOException {
-    return new DelegatingSeekableInputStream(new 
SeekableByteArrayInputStream(this.data)) {
-      @Override
-      public void seek(long newPos) throws IOException {
-        ((SeekableByteArrayInputStream) this.getStream()).setPos((int) newPos);
-      }
+    if (isLocal) {
+      // Native Parquet implementation
+      return localInputFile.newStream();
+    } else {
+      // For remote file, DelegatingSeekableInputStream handles basic 
read/skip operations
+      return new DelegatingSeekableInputStream(new 
VfsSeekableInputStream(fileObject)) {
+        @Override
+        public void seek(long newPos) throws IOException {
+          ((VfsSeekableInputStream) getStream()).seek(newPos);
+        }
 
-      @Override
-      public long getPos() throws IOException {
-        return ((SeekableByteArrayInputStream) this.getStream()).getPos();
-      }
-    };
+        @Override
+        public long getPos() throws IOException {
+          return ((VfsSeekableInputStream) getStream()).getPos();
+        }
+      };
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (!isLocal && fileObject != null) {
+      fileObject.getContent().close();
+    }
   }
 
   @Override
@@ -57,17 +86,73 @@ public class ParquetStream implements InputFile {
     return "ParquetStream of file '" + filename + "'";
   }
 
-  private static class SeekableByteArrayInputStream extends 
ByteArrayInputStream {
-    public SeekableByteArrayInputStream(byte[] buf) {
-      super(buf);
+  // SeekableInputStream implementation for remote files
+  private static class VfsSeekableInputStream extends InputStream {
+
+    private final FileObject fileObject;
+    private InputStream currentStream;
+    private long pos = 0;
+
+    public VfsSeekableInputStream(FileObject fileObject) throws IOException {
+      this.fileObject = fileObject;
+      this.currentStream = HopVfs.getInputStream(fileObject);
     }
 
-    public void setPos(int pos) {
-      this.pos = pos;
+    // Reads a single byte and advances the position by 1
+    @Override
+    public int read() throws IOException {
+      int b = currentStream.read();
+      if (b != -1) {
+        pos++;
+      }
+
+      return b;
+    }
+
+    // Reads up to len bytes into buffer b starting at offset off
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+      int n = currentStream.read(b, off, len);
+      if (n > 0) {
+        pos += n;
+      }
+
+      return n;
+    }
+
+    public long getPos() {
+      return pos;
+    }
+
+    public void seek(long newPos) throws IOException {
+      currentStream.close();
+      currentStream = HopVfs.getInputStream(fileObject);
+      pos = 0;
+      long remaining = newPos;
+      while (remaining > 0) {
+        long skipped = currentStream.skip(remaining);
+        if (skipped > 0) {
+          // Advance by the number of bytes skipped
+          remaining -= skipped;
+          // If skip() returns 0, stream may be blocked
+        } else if (skipped == 0) {
+          // Try reading one byte manually to advance
+          if (currentStream.read() == -1) {
+            break;
+          }
+          // One byte consumed via read(), decrement remaining accordingly
+          remaining--;
+        } else {
+          break;
+        }
+      }
+      // Update position to reflect where we actually ended up
+      pos = newPos - remaining;
     }
 
-    public int getPos() {
-      return this.pos;
+    @Override
+    public void close() throws IOException {
+      currentStream.close();
     }
   }
 }
diff --git 
a/plugins/tech/parquet/src/test/java/org/apache/hop/parquet/transforms/output/ParquetTestUtil.java
 
b/plugins/tech/parquet/src/test/java/org/apache/hop/parquet/transforms/output/ParquetTestUtil.java
index 5cc01b5642..45fd8c19c6 100644
--- 
a/plugins/tech/parquet/src/test/java/org/apache/hop/parquet/transforms/output/ParquetTestUtil.java
+++ 
b/plugins/tech/parquet/src/test/java/org/apache/hop/parquet/transforms/output/ParquetTestUtil.java
@@ -19,17 +19,19 @@ package org.apache.hop.parquet.transforms.output;
 
 import java.io.IOException;
 import java.net.URISyntaxException;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import org.apache.commons.vfs2.FileObject;
 import org.apache.hop.core.RowMetaAndData;
 import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.exception.HopFileException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.core.variables.Variables;
+import org.apache.hop.core.vfs.HopVfs;
 import org.apache.hop.parquet.transforms.input.ParquetField;
 import org.apache.hop.parquet.transforms.input.ParquetInputMeta;
 import org.apache.hop.parquet.transforms.input.ParquetReadSupport;
@@ -76,9 +78,10 @@ final class ParquetTestUtil {
   }
 
   static List<RowMetaAndData> readAllRows(String filename, List<ParquetField> 
fields)
-      throws IOException {
-    byte[] data = Files.readAllBytes(Path.of(filename));
-    ParquetStream inputFile = new ParquetStream(data, filename);
+      throws IOException, HopFileException {
+    FileObject fileObject = HopVfs.getFileObject(filename);
+    ParquetStream inputFile = new ParquetStream(fileObject, filename);
+
     ParquetReadSupport readSupport = new ParquetReadSupport(fields);
     try (ParquetReader<RowMetaAndData> reader =
         new ParquetReaderBuilder<>(readSupport, inputFile).build()) {

Reply via email to