SQOOP-2399: BigDecimalSplitter java.lang.ArrayIndexOutOfBoundsException

   (Sowmya Ramesh via Venkat Ranganathan)
diff --git a/src/java/org/apache/sqoop/mapreduce/db/BigDecimalSplitter.java b/src/java/org/apache/sqoop/mapreduce/db/BigDecimalSplitter.java
index ebe6c40..a8db2a3 100644
--- a/src/java/org/apache/sqoop/mapreduce/db/BigDecimalSplitter.java
+++ b/src/java/org/apache/sqoop/mapreduce/db/BigDecimalSplitter.java
@@ -31,6 +31,7 @@
 import com.cloudera.sqoop.config.ConfigurationHelper;
 import com.cloudera.sqoop.mapreduce.db.DBSplitter;
 import com.cloudera.sqoop.mapreduce.db.DataDrivenDBInputFormat;
+import org.apache.sqoop.validation.ValidationException;
 
 /**
  * Implement DBSplitter over BigDecimal values.
@@ -39,7 +40,7 @@
   private static final Log LOG = LogFactory.getLog(BigDecimalSplitter.class);
 
   public List<InputSplit> split(Configuration conf, ResultSet results,
-      String colName) throws SQLException {
+      String colName) throws SQLException, ValidationException {
 
     BigDecimal minVal = results.getBigDecimal(1);
     BigDecimal maxVal = results.getBigDecimal(2);
@@ -140,7 +141,12 @@
       curVal = curVal.add(splitSize);
     }
 
-    if (splits.get(splits.size() - 1).compareTo(maxVal) != 0
+    /*
+     * If the sort order and collation of the char columns differ we can have
+     * a situation where minVal > maxVal and splits can be empty list.
+     */
+
+    if ((splits.size() > 1 && splits.get(splits.size() - 1).compareTo(maxVal) != 0)
         || splits.size() == 1) {
       // We didn't end on the maxVal. Add that to the end of the list.
       splits.add(maxVal);
diff --git a/src/java/org/apache/sqoop/mapreduce/db/DBSplitter.java b/src/java/org/apache/sqoop/mapreduce/db/DBSplitter.java
index b121d4b..9ea8caf 100644
--- a/src/java/org/apache/sqoop/mapreduce/db/DBSplitter.java
+++ b/src/java/org/apache/sqoop/mapreduce/db/DBSplitter.java
@@ -23,6 +23,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.mapreduce.InputSplit;
+import org.apache.sqoop.validation.ValidationException;
 
 /**
  * DBSplitter will generate DBInputSplits to use with DataDrivenDBInputFormat.
@@ -40,5 +41,5 @@
    * type), determine a set of splits that span the given values.
    */
   List<InputSplit> split(Configuration conf, ResultSet results, String colName)
-      throws SQLException;
+      throws SQLException, ValidationException;
 }
diff --git a/src/java/org/apache/sqoop/mapreduce/db/DataDrivenDBInputFormat.java b/src/java/org/apache/sqoop/mapreduce/db/DataDrivenDBInputFormat.java
index db96e41..136b30a 100644
--- a/src/java/org/apache/sqoop/mapreduce/db/DataDrivenDBInputFormat.java
+++ b/src/java/org/apache/sqoop/mapreduce/db/DataDrivenDBInputFormat.java
@@ -51,7 +51,7 @@
 import com.cloudera.sqoop.mapreduce.db.FloatSplitter;
 import com.cloudera.sqoop.mapreduce.db.IntegerSplitter;
 import com.cloudera.sqoop.mapreduce.db.TextSplitter;
-import com.cloudera.sqoop.mapreduce.db.DBInputFormat.DBInputSplit;
+import org.apache.sqoop.validation.ValidationException;
 
 /**
  * A InputFormat that reads input data from an SQL table.
@@ -197,8 +197,12 @@
           + " type: " + sqlDataType);
       }
 
-      return splitter.split(job.getConfiguration(), results,
-          getDBConf().getInputOrderBy());
+      try {
+        return splitter.split(job.getConfiguration(), results,
+                  getDBConf().getInputOrderBy());
+      } catch (ValidationException e) {
+        throw new IOException(e);
+      }
     } catch (SQLException e) {
       throw new IOException(e);
     } finally {
diff --git a/src/java/org/apache/sqoop/mapreduce/db/TextSplitter.java b/src/java/org/apache/sqoop/mapreduce/db/TextSplitter.java
index d3085cd..9896d95 100644
--- a/src/java/org/apache/sqoop/mapreduce/db/TextSplitter.java
+++ b/src/java/org/apache/sqoop/mapreduce/db/TextSplitter.java
@@ -31,6 +31,7 @@
 import com.cloudera.sqoop.config.ConfigurationHelper;
 import com.cloudera.sqoop.mapreduce.db.BigDecimalSplitter;
 import com.cloudera.sqoop.mapreduce.db.DataDrivenDBInputFormat;
+import org.apache.sqoop.validation.ValidationException;
 
 /**
  * Implement DBSplitter over text strings.
@@ -59,7 +60,7 @@
    * points, then map the resulting floating point values back into strings.
    */
   public List<InputSplit> split(Configuration conf, ResultSet results,
-      String colName) throws SQLException {
+      String colName) throws SQLException, ValidationException {
 
     LOG.warn("Generating splits for a textual index column.");
     LOG.warn("If your database sorts in a case-insensitive order, "
@@ -146,11 +147,16 @@
   }
 
   public List<String> split(int numSplits, String minString,
-      String maxString, String commonPrefix) throws SQLException {
+      String maxString, String commonPrefix) throws SQLException, ValidationException {
 
     BigDecimal minVal = stringToBigDecimal(minString);
     BigDecimal maxVal = stringToBigDecimal(maxString);
 
+
+    if (minVal.compareTo(maxVal) > 0) {
+        throw new ValidationException( minVal + " is greater than " + maxVal);
+    }
+
     List<BigDecimal> splitPoints = split(
         new BigDecimal(numSplits), minVal, maxVal);
     List<String> splitStrings = new ArrayList<String>();
diff --git a/src/test/org/apache/sqoop/mapreduce/db/TestTextSplitter.java b/src/test/org/apache/sqoop/mapreduce/db/TestTextSplitter.java
index 9c007d3..5cfb0a5 100644
--- a/src/test/org/apache/sqoop/mapreduce/db/TestTextSplitter.java
+++ b/src/test/org/apache/sqoop/mapreduce/db/TestTextSplitter.java
@@ -24,6 +24,8 @@
 import com.cloudera.sqoop.mapreduce.db.TextSplitter;
 
 import junit.framework.TestCase;
+import junit.framework.Test;
+import org.apache.sqoop.validation.ValidationException;
 
 /**
  * Test that the TextSplitter implementation creates a sane set of splits.
@@ -113,7 +115,7 @@
     assertEquals("AVeryLon", out);
   }
 
-  public void testAlphabetSplit() throws SQLException {
+  public void testAlphabetSplit() throws SQLException, ValidationException {
     // This should give us 25 splits, one per letter.
     TextSplitter splitter = new TextSplitter();
     List<String> splits = splitter.split(25, "A", "Z", "");
@@ -123,7 +125,18 @@
     assertArrayEquals(expected, splits.toArray(new String [0]));
   }
 
-  public void testCommonPrefix() throws SQLException {
+    public void testAlphabetSplitWhenMinStringGreaterThanMaxString() throws SQLException {
+        TextSplitter splitter = new TextSplitter();
+        try {
+            splitter.split(4, "Z", "A", "");
+            fail();
+        } catch (ValidationException e) {
+            // expected
+            assertTrue(true);
+        }
+    }
+
+  public void testCommonPrefix() throws SQLException, ValidationException {
     // Splits between 'Hand' and 'Hardy'
     TextSplitter splitter = new TextSplitter();
     List<String> splits = splitter.split(5, "nd", "rdy", "Ha");