diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/LoaderTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/LoaderTests.java index 2aacc0ada4..88acdf8463 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/LoaderTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/LoaderTests.java @@ -38,6 +38,7 @@ package org.jooq.test._.testcases; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; +import static org.jooq.SQLDialect.ORACLE; import static org.jooq.impl.Factory.count; import java.sql.SQLException; @@ -191,16 +192,16 @@ extends BaseTest> extends LoaderLo */ @Support LoaderCSVOptionsStep separator(char separator); + + /** + * Specify the input string representation of NULL. + *

+ * By default, this is set to null, which means that all empty + * strings are loaded into the database as such. In some databases (e.g. + * {@link SQLDialect#ORACLE}), this is effectively the same as loading + * NULL. + *

+ * In order to treat empty strings as null, you can set the + * nullString to "". If the null string is + * overridden with something like {null}, for instance, then + * empty strings will also be loaded as such by jOOQ. + */ + @Support + LoaderCSVOptionsStep nullString(String nullString); } diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index f3d348e9f6..b33b3100b9 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -1724,7 +1724,7 @@ public interface Result extends FieldProvider, List, Attach /** * Get a simple formatted representation of this result as CSV. *

- * This is the same as calling formatCSV(',') + * This is the same as calling formatCSV(',', "") * * @return The formatted result */ @@ -1732,12 +1732,23 @@ public interface Result extends FieldProvider, List, Attach /** * Get a simple formatted representation of this result as CSV. + *

+ * This is the same as calling formatCSV(delimiter, "") * * @param delimiter The delimiter to use between records * @return The formatted result */ String formatCSV(char delimiter); + /** + * Get a simple formatted representation of this result as CSV. + * + * @param delimiter The delimiter to use between records + * @param nullString A special string for encoding NULL values. + * @return The formatted result + */ + String formatCSV(char delimiter, String nullString); + /** * Get a simple formatted representation of this result as a JSON array of * array. The format is the following:

diff --git a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java
index 9e75a373ae..812867cce7 100644
--- a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java
@@ -63,6 +63,7 @@ import org.jooq.Table;
 import org.jooq.TableRecord;
 import org.jooq.UpdatableTable;
 import org.jooq.exception.DataAccessException;
+import org.jooq.tools.StringUtils;
 import org.jooq.tools.csv.CSVParser;
 import org.jooq.tools.csv.CSVReader;
 
@@ -113,6 +114,7 @@ class LoaderImpl> implements
     private int                     ignoreRows              = 1;
     private char                    quote                   = CSVParser.DEFAULT_QUOTE_CHARACTER;
     private char                    separator               = CSVParser.DEFAULT_SEPARATOR;
+    private String                  nullString              = null;
     private Field[]              fields;
     private boolean[]               mainKey;
 
@@ -306,6 +308,12 @@ class LoaderImpl> implements
         return this;
     }
 
+    @Override
+    public final LoaderImpl nullString(String n) {
+        this.nullString = n;
+        return this;
+    }
+
     // -------------------------------------------------------------------------
     // XML configuration
     // -------------------------------------------------------------------------
@@ -340,6 +348,14 @@ class LoaderImpl> implements
             // TODO: When running in COMMIT_AFTER > 1 or COMMIT_ALL mode, then
             // it might be better to bulk load / merge n records
             rowloop: while ((row = reader.readNext()) != null) {
+
+                // [#1627] Handle NULL values
+                for (int i = 0; i < row.length; i++) {
+                    if (StringUtils.equals(nullString, row[i])) {
+                        row[i] = null;
+                    }
+                }
+
                 processed++;
                 InsertQuery insert = create.insertQuery(table);
 
diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java
index c04ba5c43c..083f9dc958 100644
--- a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java
@@ -1057,17 +1057,22 @@ class ResultImpl implements Result, AttachableInternal {
 
     @Override
     public final String formatCSV() {
-        return formatCSV(',');
+        return formatCSV(',', "");
     }
 
     @Override
     public final String formatCSV(char delimiter) {
+        return formatCSV(delimiter, "");
+    }
+
+    @Override
+    public final String formatCSV(char delimiter, String nullString) {
         StringBuilder sb = new StringBuilder();
 
         String sep1 = "";
         for (Field field : getFields()) {
             sb.append(sep1);
-            sb.append(formatCSV0(field.getName()));
+            sb.append(formatCSV0(field.getName(), ""));
 
             sep1 = Character.toString(delimiter);
         }
@@ -1078,7 +1083,7 @@ class ResultImpl implements Result, AttachableInternal {
             String sep2 = "";
             for (Field field : getFields()) {
                 sb.append(sep2);
-                sb.append(formatCSV0(record.getValue(field)));
+                sb.append(formatCSV0(record.getValue(field), nullString));
 
                 sep2 = Character.toString(delimiter);
             }
@@ -1089,11 +1094,16 @@ class ResultImpl implements Result, AttachableInternal {
         return sb.toString();
     }
 
-    private final String formatCSV0(Object value) {
+    private final String formatCSV0(Object value, String nullString) {
 
         // Escape null and empty strings
         if (value == null || "".equals(value)) {
-            return "\"\"";
+            if (StringUtils.isEmpty(nullString)) {
+                return "\"\"";
+            }
+            else {
+                return nullString;
+            }
         }
 
         String result = format0(value);