IMPALA-9249: Fix ORC scanner crash when root type is not struct
The root type should be struct as far as I know, and this was
checked with a DCHECK, leading to crashes in fuzz tests. This
change replaced the DCHECK with returning an error message.
Testing:
- added corrupt ORC file and e2e test
Change-Id: I7fba8cffbcdf8f647e27e2d5ee9e6716a4492b9b
Reviewed-on: http://gerrit.cloudera.org:8080/15021
Reviewed-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
diff --git a/be/src/exec/hdfs-orc-scanner.cc b/be/src/exec/hdfs-orc-scanner.cc
index 164feaa..e8e236a 100644
--- a/be/src/exec/hdfs-orc-scanner.cc
+++ b/be/src/exec/hdfs-orc-scanner.cc
@@ -184,8 +184,8 @@
// Build 'col_id_path_map_' that maps from ORC column ids to their corresponding
// SchemaPath in the table. The map is used in the constructors of OrcColumnReaders
// where we resolve SchemaPaths of the descriptors.
- OrcMetadataUtils::BuildSchemaPaths(reader_->getType(),
- scan_node_->num_partition_keys(), &col_id_path_map_);
+ RETURN_IF_ERROR(schema_resolver_->BuildSchemaPaths(scan_node_->num_partition_keys(),
+ &col_id_path_map_));
// To create OrcColumnReaders, we need the selected orc schema. It's a subset of the
// file schema: a tree of selected orc types and can only be got from an orc::RowReader
diff --git a/be/src/exec/orc-metadata-utils.cc b/be/src/exec/orc-metadata-utils.cc
index 0dd55fd..27db46d 100644
--- a/be/src/exec/orc-metadata-utils.cc
+++ b/be/src/exec/orc-metadata-utils.cc
@@ -22,20 +22,24 @@
namespace impala {
-void OrcMetadataUtils::BuildSchemaPaths(const orc::Type& root, int num_partition_keys,
+Status OrcSchemaResolver::BuildSchemaPaths(int num_partition_keys,
vector<SchemaPath>* paths) {
+ if (root_->getKind() != orc::TypeKind::STRUCT) {
+ return Status(Substitute("Corrupt ORC File '$0': root type is $1 (should be struct)",
+ filename_, root_->toString()));
+ }
SchemaPath path;
paths->push_back(path);
- DCHECK_EQ(root.getKind(), orc::TypeKind::STRUCT);
- int num_columns = root.getSubtypeCount();
+ int num_columns = root_->getSubtypeCount();
for (int i = 0; i < num_columns; ++i) {
path.push_back(i + num_partition_keys);
- BuildSchemaPath(*root.getSubtype(i), &path, paths);
+ BuildSchemaPath(*root_->getSubtype(i), &path, paths);
path.pop_back();
}
+ return Status::OK();
}
-void OrcMetadataUtils::BuildSchemaPath(const orc::Type& node, SchemaPath* path,
+void OrcSchemaResolver::BuildSchemaPath(const orc::Type& node, SchemaPath* path,
vector<SchemaPath>* paths) {
DCHECK_EQ(paths->size(), node.getColumnId());
paths->push_back(*path);
diff --git a/be/src/exec/orc-metadata-utils.h b/be/src/exec/orc-metadata-utils.h
index eac085a..d93ae6f 100644
--- a/be/src/exec/orc-metadata-utils.h
+++ b/be/src/exec/orc-metadata-utils.h
@@ -24,17 +24,6 @@
namespace impala {
-/// Utils to build a map from each orc::Type id to a SchemaPath. The map will be used in
-/// creating OrcColumnReaders.
-class OrcMetadataUtils {
- public:
- static void BuildSchemaPaths(const orc::Type& root, int num_partition_keys,
- std::vector<SchemaPath>* paths);
- private:
- static void BuildSchemaPath(const orc::Type& node, SchemaPath* path,
- std::vector<SchemaPath>* paths);
-};
-
/// Util class to resolve SchemaPaths of TupleDescriptors/SlotDescriptors into orc::Type.
class OrcSchemaResolver {
public:
@@ -47,6 +36,11 @@
/// 'missing_field' is set to true if the column is missing in the ORC file.
Status ResolveColumn(const SchemaPath& col_path, const orc::Type** node,
bool* pos_field, bool* missing_field) const;
+
+ /// Build a map from each orc::Type id to a SchemaPath. The map will be used in
+ /// creating OrcColumnReaders.
+ Status BuildSchemaPaths(int num_partition_keys, std::vector<SchemaPath>* paths);
+
private:
const HdfsTableDescriptor& tbl_desc_;
const orc::Type* const root_;
@@ -55,5 +49,8 @@
/// Validate whether the ColumnType is compatible with the orc type
Status ValidateType(const ColumnType& type, const orc::Type& orc_type) const
WARN_UNUSED_RESULT;
+
+ static void BuildSchemaPath(const orc::Type& node, SchemaPath* path,
+ std::vector<SchemaPath>* paths);
};
}
diff --git a/testdata/data/README b/testdata/data/README
index a8f20c2..f46bc9f 100644
--- a/testdata/data/README
+++ b/testdata/data/README
@@ -458,3 +458,7 @@
corrupt_schema.orc:
ORC file from IMPALA-9277, generated by fuzz test. The file contains malformed metadata.
+
+corrupt_root_type.orc:
+ORC file for IMPALA-9249, generated by fuzz test. The root type of the schema is not
+struct, which used to hit a DCHECK.
diff --git a/testdata/data/corrupt_root_type.orc b/testdata/data/corrupt_root_type.orc
new file mode 100644
index 0000000..b6fab22
--- /dev/null
+++ b/testdata/data/corrupt_root_type.orc
Binary files differ
diff --git a/tests/query_test/test_scanners.py b/tests/query_test/test_scanners.py
index 1d26463..b303a62 100644
--- a/tests/query_test/test_scanners.py
+++ b/tests/query_test/test_scanners.py
@@ -1334,15 +1334,26 @@
self.run_test_case('DataErrorsTest/orc-out-of-range-timestamp',
new_vector, unique_database)
- def test_invalid_schema(self, vector, unique_database):
- """Test scanning of ORC file with malformed schema."""
- test_files = ["testdata/data/corrupt_schema.orc"]
+ def _run_invalid_schema_test(self, unique_database, test_name, expected_error):
+ """Copies 'test_name'.orc to a table and runs a simple query. These tests should
+ cause an error during the processing of the ORC schema, so the file's columns do
+ not have to match with the table's columns.
+ """
+ test_files = ["testdata/data/%s.orc" % test_name]
create_table_and_copy_files(self.client,
"CREATE TABLE {db}.{tbl} (id BIGINT) STORED AS ORC",
- unique_database, "corrupt_schema", test_files)
+ unique_database, test_name, test_files)
err = self.execute_query_expect_failure(self.client,
- "select count(*) from {0}.{1}".format(unique_database, "corrupt_schema"))
- assert "Encountered parse error during schema selection" in str(err)
+ "select count(*) from {0}.{1}".format(unique_database, test_name))
+ assert expected_error in str(err)
+
+ def test_invalid_schema(self, vector, unique_database):
+ """Test scanning of ORC file with malformed schema."""
+ self._run_invalid_schema_test(unique_database, "corrupt_schema",
+ "Encountered parse error during schema selection")
+ self._run_invalid_schema_test(unique_database, "corrupt_root_type",
+ "root type is boolean (should be struct)")
+
class TestScannerReservation(ImpalaTestSuite):
@classmethod