From 09e27ffdb76540136e710a2026a0d77bc9a49aee Mon Sep 17 00:00:00 2001 From: Naveed Khan Date: Tue, 21 Jul 2026 17:01:18 +0530 Subject: [PATCH] count rows produced instead of getRow() in printRecords(ResultSet) --- .../org/apache/commons/csv/CSVPrinter.java | 7 +++++- .../apache/commons/csv/CSVPrinterTest.java | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java index 42c01bcc0..98c2d1071 100644 --- a/src/main/java/org/apache/commons/csv/CSVPrinter.java +++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java @@ -502,7 +502,11 @@ public void printRecords(final Object... values) throws IOException { */ public void printRecords(final ResultSet resultSet) throws SQLException, IOException { final int columnCount = resultSet.getMetaData().getColumnCount(); - while (resultSet.next() && format.useRow(resultSet.getRow())) { + // Count the rows produced here instead of ResultSet.getRow(): getRow() is the absolute cursor + // position, which is optional for TYPE_FORWARD_ONLY result sets and returns 0 there, silently + // disabling maxRows. Mirrors the row-produced counter the parser uses (CSV-327). + long rowCount = 0; + while (format.useRow(rowCount + 1) && resultSet.next()) { lock.lock(); try { for (int i = 1; i <= columnCount; i++) { @@ -523,6 +527,7 @@ public void printRecords(final ResultSet resultSet) throws SQLException, IOExcep } finally { lock.unlock(); } + rowCount++; } } diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index 423609762..83c43d421 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -51,6 +51,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Types; import java.util.Arrays; import java.util.Date; import java.util.HashSet; @@ -964,6 +965,30 @@ void testJdbcPrinterWithResultSetMetaData(final long maxRows) throws IOException } } + @Test + void testJdbcPrinterWithResultSetWithoutRowNumber() throws IOException, SQLException { + // JDBC makes ResultSet.getRow() optional for TYPE_FORWARD_ONLY result sets, where a driver may return 0. + // maxRows must still cap the output by rows produced rather than by getRow(). + final StringWriter sw = new StringWriter(); + final CSVFormat format = CSVFormat.DEFAULT.builder().setMaxRows(2).get(); + try (SimpleResultSet resultSet = new SimpleResultSet() { + @Override + public int getRow() { + return 0; + } + }) { + resultSet.addColumn("ID", Types.INTEGER, 10, 0); + for (int i = 1; i <= 4; i++) { + resultSet.addRow(i); + } + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + printer.printRecords(resultSet); + assertEquals(2, printer.getRecordCount()); + } + } + assertEquals("1" + RECORD_SEPARATOR + "2" + RECORD_SEPARATOR, sw.toString()); + } + @Test void testJira135_part1() throws IOException { final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH);