SQOOP-3206: Make sqoop fail if user uses --direct connector and tries to encode a null value when using a MySQL database
(Zach Berkowitz via Anna Szonyi)
diff --git a/src/java/org/apache/sqoop/tool/ExportTool.java b/src/java/org/apache/sqoop/tool/ExportTool.java
index 5512fa7..cd6cdf3 100644
--- a/src/java/org/apache/sqoop/tool/ExportTool.java
+++ b/src/java/org/apache/sqoop/tool/ExportTool.java
@@ -33,6 +33,7 @@
import com.cloudera.sqoop.cli.ToolOptions;
import com.cloudera.sqoop.manager.ExportJobContext;
import com.cloudera.sqoop.util.ExportException;
+import static org.apache.sqoop.manager.SupportedManagers.MYSQL;
/**
* Tool that performs HDFS exports to databases.
@@ -385,8 +386,21 @@
void vaildateDirectExportOptions(SqoopOptions options) throws InvalidOptionsException {
if (options.isDirect()) {
validateHasDirectConnectorOption(options);
+ validateDirectMysqlOptions(options);
}
}
+
+ public void validateDirectMysqlOptions(SqoopOptions options) throws InvalidOptionsException {
+ if (!MYSQL.isTheManagerTypeOf(options)) {
+ return;
+ }
+ if (options.getInNullStringValue() != null || options.getInNullNonStringValue() != null) {
+ throw new InvalidOptionsException(
+ "The --direct option is not compatible with the --input-null-string or " +
+ "--input-null-non-string command for MySQL exports");
+ }
+ }
+
private void applyNewUpdateOptions(CommandLine in, SqoopOptions out)
throws InvalidOptionsException {
if (in.hasOption(UPDATE_MODE_ARG)) {
diff --git a/src/java/org/apache/sqoop/tool/ImportTool.java b/src/java/org/apache/sqoop/tool/ImportTool.java
index 4b1b12d..78c7758 100644
--- a/src/java/org/apache/sqoop/tool/ImportTool.java
+++ b/src/java/org/apache/sqoop/tool/ImportTool.java
@@ -1120,13 +1120,20 @@
}
void validateDirectMysqlOptions(SqoopOptions options) throws InvalidOptionsException {
- if (options.getFileLayout() != SqoopOptions.FileLayout.TextFile
- && MYSQL.isTheManagerTypeOf(options)) {
+ if (!MYSQL.isTheManagerTypeOf(options)) {
+ return;
+ }
+ if (options.getFileLayout() != SqoopOptions.FileLayout.TextFile) {
throw new InvalidOptionsException(
"MySQL direct import currently supports only text output format. "
+ "Parameters --as-sequencefile --as-avrodatafile and --as-parquetfile are not "
+ "supported with --direct params in MySQL case.");
}
+ if (options.getNullStringValue() != null || options.getNullNonStringValue() != null) {
+ throw new InvalidOptionsException(
+ "The --direct option is not compatible with the --null-string or " +
+ "--null-non-string command for MySQL imports");
+ }
}
/**
* Validate the incremental import options.
diff --git a/src/test/com/cloudera/sqoop/manager/DirectMySQLExportTest.java b/src/test/com/cloudera/sqoop/manager/DirectMySQLExportTest.java
index 9fa8816..f9e3cde 100644
--- a/src/test/com/cloudera/sqoop/manager/DirectMySQLExportTest.java
+++ b/src/test/com/cloudera/sqoop/manager/DirectMySQLExportTest.java
@@ -303,6 +303,24 @@
}
}
+ @Test(expected = IOException.class)
+ public void testExportInputNullStringFailsValidate() throws IOException {
+ runExport(getArgv(true, 10, 10,
+ "--username", MySQLAuthTest.AUTH_TEST_USER,
+ "--password", MySQLAuthTest.AUTH_TEST_PASS,
+ "--connect", MySQLAuthTest.AUTH_CONNECT_STRING,
+ "--input-null-string", "null"));
+ }
+
+ @Test(expected = IOException.class)
+ public void testExportInputNullNonStringFailsValidate() throws IOException {
+ runExport(getArgv(true, 10, 10,
+ "--username", MySQLAuthTest.AUTH_TEST_USER,
+ "--password", MySQLAuthTest.AUTH_TEST_PASS,
+ "--connect", MySQLAuthTest.AUTH_CONNECT_STRING,
+ "--input-null-non-string", "null"));
+ }
+
@Ignore("Ignoring this test as staging is not supported in direct mode.")
@Override
@Test
diff --git a/src/test/com/cloudera/sqoop/manager/DirectMySQLTest.java b/src/test/com/cloudera/sqoop/manager/DirectMySQLTest.java
index a58fa17..247ce0b 100644
--- a/src/test/com/cloudera/sqoop/manager/DirectMySQLTest.java
+++ b/src/test/com/cloudera/sqoop/manager/DirectMySQLTest.java
@@ -192,13 +192,7 @@
}
String [] argv = getArgv(mysqlOutputDelims, isDirect, tableName, extraArgs);
- try {
- runImport(argv);
- } catch (IOException ioe) {
- LOG.error("Got IOException during import: " + ioe.toString());
- ioe.printStackTrace();
- fail(ioe.toString());
- }
+ runImport(argv);
File f = new File(filePath.toString());
assertTrue("Could not find imported data file: " + f, f.exists());
@@ -358,6 +352,22 @@
}
+ @Test(expected = IOException.class)
+ public void testSqoopNullStringValueFailsValidate() throws Exception {
+ String [] expectedResults = {};
+ String [] extraArgs = {"--null-string", "abc"};
+
+ doImport(false, true, getTableName(), expectedResults, extraArgs);
+ }
+
+ @Test(expected = IOException.class)
+ public void testSqoopNullNonStringValueFailsValidate() throws Exception {
+ String [] expectedResults = {};
+ String [] extraArgs = {"--null-non-string", "abc"};
+
+ doImport(false, true, getTableName(), expectedResults, extraArgs);
+ }
+
@Test
public void testJdbcEscapedColumnName() throws Exception {
// Test a JDBC-based import of a table with a column whose name is
diff --git a/src/test/org/apache/sqoop/tool/TestExportToolValidateOptions.java b/src/test/org/apache/sqoop/tool/TestExportToolValidateOptions.java
index dfe1952..0018fb1 100644
--- a/src/test/org/apache/sqoop/tool/TestExportToolValidateOptions.java
+++ b/src/test/org/apache/sqoop/tool/TestExportToolValidateOptions.java
@@ -66,6 +66,22 @@
exportTool.vaildateDirectExportOptions(options);
}
+ @Test(expected = SqoopOptions.InvalidOptionsException.class)
+ public void givenDirectImportInputNullStringThrows() throws SqoopOptions.InvalidOptionsException {
+ SqoopOptions options = stubDirectOptions(SupportedManagers.MYSQL);
+ when(options.getInNullNonStringValue()).thenReturn("abc");
+
+ exportTool.validateDirectMysqlOptions(options);
+ }
+
+ @Test(expected = SqoopOptions.InvalidOptionsException.class)
+ public void givenDirectImportInputNullNonStringThrows() throws SqoopOptions.InvalidOptionsException {
+ SqoopOptions options = stubDirectOptions(SupportedManagers.MYSQL);
+ when(options.getInNullNonStringValue()).thenReturn("abc");
+
+ exportTool.validateDirectMysqlOptions(options);
+ }
+
private SqoopOptions stubDirectOptions(SupportedManagers supportedManagers) {
return stubOptions(supportedManagers, true);
}
diff --git a/src/test/org/apache/sqoop/tool/TestValidateImportOptions.java b/src/test/org/apache/sqoop/tool/TestValidateImportOptions.java
index acf4fcf..d4084ed 100644
--- a/src/test/org/apache/sqoop/tool/TestValidateImportOptions.java
+++ b/src/test/org/apache/sqoop/tool/TestValidateImportOptions.java
@@ -44,7 +44,6 @@
when(options.getConnectString()).thenReturn(mysqlConnectionString);
importTool.validateDirectMysqlOptions(options);
verify(options, times(1)).getFileLayout();
- verifyNoMoreInteractions(options);
}
@@ -107,6 +106,22 @@
importTool.validateDirectImportOptions(options);
}
+ @Test(expected = SqoopOptions.InvalidOptionsException.class)
+ public void givenDirectImportNullNonStringThrows() throws SqoopOptions.InvalidOptionsException {
+ SqoopOptions options = stubDirectOptions(SupportedManagers.MYSQL);
+ when(options.getNullNonStringValue()).thenReturn("abc");
+
+ importTool.validateDirectMysqlOptions(options);
+ }
+
+ @Test(expected = SqoopOptions.InvalidOptionsException.class)
+ public void givenDirectImportNullStringThrows() throws SqoopOptions.InvalidOptionsException {
+ SqoopOptions options = stubDirectOptions(SupportedManagers.MYSQL);
+ when(options.getNullStringValue()).thenReturn("abc");
+
+ importTool.validateDirectMysqlOptions(options);
+ }
+
private SqoopOptions stubDirectOptions(SupportedManagers supportedManagers) {
return stubOptions(supportedManagers, true);
}