SQOOP-3353: Sqoop should not check incremental constraints for HBase imports

(Szabolcs Vasas via Boglarka Egyed)
diff --git a/src/java/org/apache/sqoop/tool/ImportTool.java b/src/java/org/apache/sqoop/tool/ImportTool.java
index ccded65..478f174 100644
--- a/src/java/org/apache/sqoop/tool/ImportTool.java
+++ b/src/java/org/apache/sqoop/tool/ImportTool.java
@@ -329,7 +329,7 @@
       }
       break;
     case DateLastModified:
-      if (options.getMergeKeyCol() == null && !options.isAppendMode()) {
+      if (shouldCheckExistingOutputDirectory(options)) {
         Path outputPath = getOutputPath(options, context.getTableName(), false);
         FileSystem fs = outputPath.getFileSystem(options.getConf());
         if (fs.exists(outputPath)) {
@@ -1189,6 +1189,14 @@
     validateAccumuloOptions(options);
   }
 
+  boolean shouldCheckExistingOutputDirectory(SqoopOptions options) {
+    return options.getMergeKeyCol() == null && !options.isAppendMode() && !isHBaseImport(options);
+  }
+
+  private boolean isHBaseImport(SqoopOptions options) {
+    return options.getHBaseTable() != null;
+  }
+
   private boolean isHiveImportNeeded(SqoopOptions options) {
     if (!options.doHiveImport()) {
       return false;
diff --git a/src/test/org/apache/sqoop/TestIncrementalImport.java b/src/test/org/apache/sqoop/TestIncrementalImport.java
index 1ab9802..e1faf35 100644
--- a/src/test/org/apache/sqoop/TestIncrementalImport.java
+++ b/src/test/org/apache/sqoop/TestIncrementalImport.java
@@ -31,6 +31,7 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.hadoop.util.ToolRunner;
 import org.apache.sqoop.metastore.SavedJobsTestBase;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -275,20 +276,35 @@
     }
   }
 
+  private Path getTablePath(String tableName) {
+    Path warehouse = new Path(BaseSqoopTestCase.LOCAL_WAREHOUSE_DIR);
+    return new Path(warehouse, tableName);
+  }
+
+  private FileSystem getLocalFileSystem() throws IOException {
+    return FileSystem.getLocal(new Configuration());
+  }
+
   /**
    * Delete all files in a directory for a table.
    */
   public void clearDir(String tableName) {
     try {
-      FileSystem fs = FileSystem.getLocal(new Configuration());
-      Path warehouse = new Path(BaseSqoopTestCase.LOCAL_WAREHOUSE_DIR);
-      Path tableDir = new Path(warehouse, tableName);
+      FileSystem fs = getLocalFileSystem();
+      Path tableDir = getTablePath(tableName);
       fs.delete(tableDir, true);
     } catch (Exception e) {
       fail("Got unexpected exception: " + StringUtils.stringifyException(e));
     }
   }
 
+  public void createDir(String tableName) throws IOException {
+    FileSystem fs = FileSystem.getLocal(new Configuration());
+    Path warehouse = new Path(BaseSqoopTestCase.LOCAL_WAREHOUSE_DIR);
+    Path tableDir = new Path(warehouse, tableName);
+    fs.mkdirs(tableDir);
+  }
+
   /**
    * Look at a directory that should contain files full of an imported 'id'
    * column. Assert that all numbers in [0, expectedNums) are present
@@ -840,6 +856,25 @@
   }
 
   @Test
+  public void testLastModifiedImportWithExistingOutputDirectoryFails() throws Exception {
+    final String TABLE_NAME = "failWithExistingOutputDirectory";
+
+    createDir(TABLE_NAME);
+
+    Timestamp thePast = new Timestamp(System.currentTimeMillis() - 100);
+    createTimestampTable(TABLE_NAME, 10, thePast);
+
+    List<String> args = getArgListForTable(TABLE_NAME, true, false);
+
+    SqoopOptions options = new SqoopOptions(newConf());
+    options.setThrowOnError(true);
+
+    thrown.expectMessage("--merge-key or --append is required when using --incremental lastmodified and the output directory exists.");
+    Sqoop sqoop = new Sqoop(new ImportTool(), options.getConf(), options);
+    ToolRunner.run(sqoop.getConf(), sqoop, args.toArray(new String[0]));
+  }
+
+  @Test
   public void testNoImportFromTheFuture() throws Exception {
     // If last-modified dates for writes are serialized to be in the
     // future w.r.t. an import, do not import these rows.
diff --git a/src/test/org/apache/sqoop/tool/TestImportTool.java b/src/test/org/apache/sqoop/tool/TestImportTool.java
index 3bdc5c6..8c2be3b 100644
--- a/src/test/org/apache/sqoop/tool/TestImportTool.java
+++ b/src/test/org/apache/sqoop/tool/TestImportTool.java
@@ -21,7 +21,9 @@
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
@@ -38,6 +40,7 @@
 import org.apache.sqoop.hive.HiveClientFactory;
 import org.apache.sqoop.util.ExpectedLogMessage;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.theories.DataPoints;
@@ -59,6 +62,23 @@
   @Rule
   public ExpectedLogMessage logMessage = new ExpectedLogMessage();
 
+  private ImportTool importTool;
+
+  private ImportTool importToolSpy;
+
+  private CodeGenTool codeGenTool;
+
+  private HiveClientFactory hiveClientFactory;
+
+  @Before
+  public void before() {
+    codeGenTool = mock(CodeGenTool.class);
+    hiveClientFactory = mock(HiveClientFactory.class);
+
+    importTool = new ImportTool("import", codeGenTool, false, hiveClientFactory);
+    importToolSpy = spy(importTool);
+  }
+
   @Theory
   public void esnureTransactionIsolationLevelsAreMappedToTheRightValues(Object[] values)
       throws Exception {
@@ -74,9 +94,7 @@
     final String actualSchemaString = "actualSchema";
     final String errorMessage = "Import failed";
 
-    ImportTool importTool = spy(new ImportTool("import", mock(CodeGenTool.class), false, mock(HiveClientFactory.class)));
-
-    doReturn(true).when(importTool).init(any(SqoopOptions.class));
+    doReturn(true).when(importToolSpy).init(any(SqoopOptions.class));
 
     Schema writtenWithSchema = mock(Schema.class);
     when(writtenWithSchema.toString()).thenReturn(writtenWithSchemaString);
@@ -84,13 +102,13 @@
     when(actualSchema.toString()).thenReturn(actualSchemaString);
 
     AvroSchemaMismatchException expectedException = new AvroSchemaMismatchException(errorMessage, writtenWithSchema, actualSchema);
-    doThrow(expectedException).when(importTool).importTable(any(SqoopOptions.class));
+    doThrow(expectedException).when(importToolSpy).importTable(any(SqoopOptions.class));
 
     SqoopOptions sqoopOptions = mock(SqoopOptions.class);
     when(sqoopOptions.doHiveImport()).thenReturn(true);
 
     logMessage.expectError(expectedException.getMessage());
-    int result = importTool.run(sqoopOptions);
+    int result = importToolSpy.run(sqoopOptions);
     assertEquals(1, result);
   }
 
@@ -106,4 +124,18 @@
     Assert.fail("testExternalTableNoHiveImportThrowsException unit test failed!");
   }
 
+  @Test
+  public void testShouldCheckExistingOutputDirectoryReturnsFalseForHBaseImport() {
+    SqoopOptions sqoopOptions = mock(SqoopOptions.class);
+    when(sqoopOptions.getHBaseTable()).thenReturn("hbasetable");
+
+    assertFalse(importTool.shouldCheckExistingOutputDirectory(sqoopOptions));
+  }
+
+  @Test
+  public void testShouldCheckExistingOutputDirectoryReturnsTrueForNonHBaseImport() {
+    SqoopOptions sqoopOptions = mock(SqoopOptions.class);
+
+    assertTrue(importTool.shouldCheckExistingOutputDirectory(sqoopOptions));
+  }
 }