diff --git a/h2/src/main/org/h2/tools/Csv.java b/h2/src/main/org/h2/tools/Csv.java index f6d2189ee8..24097b0440 100644 --- a/h2/src/main/org/h2/tools/Csv.java +++ b/h2/src/main/org/h2/tools/Csv.java @@ -555,7 +555,12 @@ public Object[] readRow() throws SQLException { } } if (i < row.length) { - row[i++] = v; + // Empty Strings should be NULL + // in order to prevent conversion of zero-length String + // to Number + row[i++] = v!=null && v.length() > 0 + ? v + : null; } if (endOfLine) { break; diff --git a/h2/src/test/org/h2/test/db/TestCsv.java b/h2/src/test/org/h2/test/db/TestCsv.java index c6ceef4044..f857b41698 100644 --- a/h2/src/test/org/h2/test/db/TestCsv.java +++ b/h2/src/test/org/h2/test/db/TestCsv.java @@ -15,6 +15,7 @@ import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.sql.Connection; +import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -70,6 +71,8 @@ public void test() throws Exception { testAsTable(); testRead(); testPipe(); + testReadEmptyNumbers1(); + testReadEmptyNumbers2(); deleteDb("csv"); } @@ -316,7 +319,7 @@ private void testNull() throws Exception { assertEquals("D", meta.getColumnLabel(4)); assertTrue(rs.next()); assertEquals(null, rs.getString(1)); - assertEquals("", rs.getString(2)); + assertEquals(null, rs.getString(2)); // null is never quoted assertEquals("\\N", rs.getString(3)); // an empty string is always parsed as null @@ -366,8 +369,8 @@ private void testRandomData() throws SQLException { for (int i = 0; i < len; i++) { assertTrue(rs.next()); String[] pair = list.get(i); - assertEquals(pair[0], rs.getString(1)); - assertEquals(pair[1], rs.getString(2)); + assertEquals(pair[0]!=null && pair[0].isEmpty() ? null : pair[0], rs.getString(1)); + assertEquals(pair[1]!=null && pair[1].isEmpty() ? null : pair[1], rs.getString(2)); } assertFalse(rs.next()); conn.close(); @@ -520,7 +523,7 @@ private void testRead() throws Exception { assertEquals(null, rs.getString(1)); assertEquals("abc\"", rs.getString(2)); assertEquals(null, rs.getString(3)); - assertEquals("", rs.getString(4)); + assertEquals(null, rs.getString(4)); assertTrue(rs.next()); assertEquals("1", rs.getString(1)); assertEquals("2", rs.getString(2)); @@ -581,5 +584,59 @@ private void testWriteRead() throws SQLException { conn.close(); FileUtils.delete(getBaseDir() + "/testRW.csv"); } + + /** + * Reads a CSV file with a Number Column, having empty Cells + * Those empty Cells must be returned as NULL but not as a Zero-length + * String or else the Number conversion will fail. + * + * Furthermore, number of rows still must be correct when such an empty Cell + * has been found. + * + * @throws java.lang.Exception + */ + private void testReadEmptyNumbers1() throws Exception { + String fileName = getBaseDir() + "/test.csv"; + FileUtils.delete(fileName); + OutputStream out = FileUtils.newOutputStream(fileName, false); + byte[] b = ("\"TEST\"\n\"100.22\"\n\"\"\n").getBytes(); + out.write(b, 0, b.length); + out.close(); + + ResultSet rs = new Csv().read(fileName, null, "UTF8"); + assertTrue(rs.next()); + assertNotNull(rs.getString(1)); + + assertTrue(rs.next()); + assertNull(rs.getString(1)); + + assertFalse(rs.next()); + + FileUtils.delete(fileName); + } + + /** + * Insert a CSV with empty Number Cells into a Table with NUMERIC columns + * The empty Cell must return NULL to prevent failure from the String to + * Number conversion + * + * @throws java.lang.Exception + */ + private void testReadEmptyNumbers2() throws Exception { + String fileName = getBaseDir() + "/test.csv"; + FileUtils.delete(fileName); + OutputStream out = FileUtils.newOutputStream(fileName, false); + byte[] b = ("\"TEST\"\n\"100.22\"\n\"\"").getBytes(); + out.write(b, 0, b.length); + out.close(); + + deleteDb("csv"); + Connection conn = DriverManager.getConnection("jdbc:h2:mem:test"); + Statement stat = conn.createStatement(); + stat.execute("CREATE TABLE TEST(TEST DECIMAL(12,2) NULL)"); + stat.execute("INSERT INTO TEST SELECT * FROM CsvRead('" + fileName + "')"); + + FileUtils.delete(fileName); + } }