Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Parquet TupleDomain using ColumnDescriptor #6892

Merged
merged 1 commit into from
Apr 13, 2017

Conversation

zhenxiao
Copy link
Collaborator

Currently Parquet TupleDomain is constructed based on HiveColumnHandle. This would not work if Nested predicate are pushed down, e.g.

select s.a
from t
where s.b > 10

This patch construct Parquet TupleDomain with Parquet's ColumnDescriptor, so that it could work with nested predicate pushdown

@zhenxiao
Copy link
Collaborator Author

@nezihyigitbasi @dain this is a first step to support nested predicate pushdown for Parquet in Presto. This PR is to make Parquet TupleDomain based on Parquet ColumnDescriptor, so that when nested predicates are pushed down, it could easily work

@zhenxiao
Copy link
Collaborator Author

@dain @nezihyigitbasi kindly ping

@zhenxiao zhenxiao force-pushed the parquet-tuple-domain branch from eef5773 to fb3cf7a Compare January 7, 2017 06:50
@zhenxiao zhenxiao changed the title Support TupleDomain for Parquet Support Parquet TupleDomain using ColumnDescriptor Jan 7, 2017
@zhenxiao zhenxiao force-pushed the parquet-tuple-domain branch 2 times, most recently from 2dfa193 to 470f87a Compare January 10, 2017 01:39
@zhenxiao zhenxiao force-pushed the parquet-tuple-domain branch 2 times, most recently from b3aec2e to b8e275f Compare January 11, 2017 21:57
@zhenxiao
Copy link
Collaborator Author

@dain @nezihyigitbasi kindly ping

@zhenxiao zhenxiao force-pushed the parquet-tuple-domain branch from b8e275f to 71e17e6 Compare January 25, 2017 13:57
@zhenxiao
Copy link
Collaborator Author

@dain @nezihyigitbasi could you please review when you are free?

@zhenxiao
Copy link
Collaborator Author

zhenxiao commented Feb 8, 2017

@nezihyigitbasi @dain kindly ping

1 similar comment
@zhenxiao
Copy link
Collaborator Author

@nezihyigitbasi @dain kindly ping

@dain
Copy link
Contributor

dain commented Mar 10, 2017

In an offline discussion we decided that @zhenxiao was going to investigate using synthetic virtual-columns in the connector to enable this push-down feature.

@dain dain removed their request for review March 10, 2017 18:43
@zhenxiao
Copy link
Collaborator Author

Hi @dain:
This PR is to make Parquet TupleDomain based on Parquet ColumnDescriptor, instead of index based. It is purely Parquet stuff. This is a preparation for nested predicate pushdown stuff. It is beneficial for existing Parquet stuff as well.

Could you please review this first?

@dain dain assigned dain and unassigned zhenxiao Mar 10, 2017
@dain dain self-requested a review March 10, 2017 20:23
@dain
Copy link
Contributor

dain commented Mar 10, 2017

Ah ok. Will do.

@dain dain assigned nezihyigitbasi and unassigned dain Mar 17, 2017
@dain dain removed their request for review March 17, 2017 19:10
@zhenxiao
Copy link
Collaborator Author

zhenxiao commented Apr 3, 2017

@dain @nezihyigitbasi kindly ping

@nezihyigitbasi
Copy link
Contributor

@zhenxiao I will take a look at this shortly.

Copy link
Contributor

@nezihyigitbasi nezihyigitbasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhenxiao I did a first pass, back to you.

return index;
}

public static Type getPrestoType(RichColumnDescriptor descriptor)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't handle the decimal type correctly, please see Parquet spec and my recent related fix 37c57c9

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you, get it resolved

@@ -348,8 +350,9 @@ public void close()
long firstDataPage = block.getColumns().get(0).getFirstDataPageOffset();
if (firstDataPage >= start && firstDataPage < start + length) {
if (predicatePushdownEnabled) {
ParquetPredicate parquetPredicate = buildParquetPredicate(columns, effectivePredicate, fileMetaData.getSchema(), typeManager);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that the typeManager is not used anymore in this method we can remove it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get it, resolved

}

return new TupleDomainParquetPredicate<>(effectivePredicate, columnReferences.build());
ImmutableMap.Builder<ColumnDescriptor, Domain> predicate = ImmutableMap.builder();
for (Map.Entry<HiveColumnHandle, Domain> entry : effectivePredicate.getDomains().get().entrySet()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static import Map.Entry

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, updated

import static com.google.common.base.MoreObjects.toStringHelper;
import static java.util.Objects.requireNonNull;

public class ParquetColumnReference
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class already has access to the column descriptor so we shouldn't need to pass the Presto type as well since Presto type can be derived from that (like you do in getPrestoType()). So I guess we can move getPrestoType() logic to this class.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get it. this class not needed at all. Will delete and use RichColumnDescriptor directly

@@ -39,16 +40,14 @@ public boolean matches(Map<Integer, ParquetDictionaryDescriptor> dictionariesByC
*
* @param numberOfRows the number of rows in the segment; this can be used with
* Statistics to determine if a column is only null
* @param statisticsByColumnIndex statistics for column by ordinal position
* in the file; this will match the field order from the hive metastore
* @param statistics statistics for column by Parquet Column Descriptor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@param statistics column statistics

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

if (columnStatistics != null) {
statistics.put(ordinal, columnStatistics);
String[] paths = columnMetaData.getPath().toArray();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need the paths variable, you can just pass Arrays.asList(columnMetaData.getPath().toArray()) below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get it. Updated

dictionaries.put(ordinal, new ParquetDictionaryDescriptor(columnDescriptor, dictionaryPage));
dataSource.readFully(columnMetaData.getStartingPos(), buffer);
Optional<ParquetDictionaryPage> dictionaryPage = readDictionaryPage(buffer, columnMetaData.getCodec());
dictionaries.put(columnDescriptor, new ParquetDictionaryDescriptor(columnDescriptor, dictionaryPage));
}
catch (IOException ignored) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although not entirely related to this patch, why are we ignoring the IOException here? If we get an exception we will silently skip populating dictionaries for that particular column.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if hitting any problem when trying to read the dictionary page, we will just silently skip, and will not use that dictionary to try skip reading row groups

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK as long as we are not messing up while we read.

for (ColumnChunkMetaData columnMetaData : blockMetadata.getColumns()) {
String[] paths = columnMetaData.getPath().toArray();
Optional<RichColumnDescriptor> descriptor = getDescriptor(fileSchema, requestedSchema, Arrays.asList(paths));
if (descriptor.isPresent()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen if the descriptor is not present and we return an empty map of dictionaries?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only build dictionaries when descriptor exist, otherwise, dictionaries is empty, will scan file, dictionary predicate will not apply

return effectivePredicate.getDomains().get().keySet().stream()
.map(HiveColumnHandle::getName)
.anyMatch(columnName::equals);
return parquetTupleDomain.getDomains().get().keySet().contains(columnDescriptor);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getDomains() returns an Optional, are we sure it's non-empty? If yes, we should better assert that (com.google.common.base.Verify::verify).

throw new ParquetDecodingException("Parquet type FIXED_LEN_BYTE_ARRAY supported as DECIMAL; got " + descriptor.getPrimitiveType().getOriginalType());
}
default:
throw new ParquetDecodingException("Presto does not have type for : " + descriptor.getType());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw new PrestoException(NOT_SUPPORTED, "Unsupported parquet type: " + descriptor.getType());

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, updated

@zhenxiao zhenxiao force-pushed the parquet-tuple-domain branch from 71e17e6 to bbed115 Compare April 5, 2017 03:50
@zhenxiao
Copy link
Collaborator Author

zhenxiao commented Apr 5, 2017

thank you @nezihyigitbasi
get comments addressed

Copy link
Contributor

@nezihyigitbasi nezihyigitbasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM except some minor comments. BTW do we need similar changes in the new reader for nested predicate pushdown support?

if (type.getName().equalsIgnoreCase(columnName)) {
return type;
}
}

return null;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that we have this method in ParquetTypeUtils we can update ParquetColumnReader to also call this one instead of maintaining two copies.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, get it updated

return effectivePredicate.getDomains().get().keySet().stream()
.map(HiveColumnHandle::getName)
.anyMatch(columnName::equals);
verify(parquetTupleDomain.getDomains().isPresent(), "Parquet TupleDomain should not be empty");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"parquetTupleDomain is empty"

@zhenxiao zhenxiao force-pushed the parquet-tuple-domain branch from bbed115 to 5eda135 Compare April 11, 2017 02:52
@zhenxiao
Copy link
Collaborator Author

thank you @nezihyigitbasi get comments addressed
Yes, need to do something like virtual column to push down nested columns. New Parquet Reader part support is there

@nezihyigitbasi
Copy link
Contributor

@dain I think this looks good.

Copy link
Contributor

@dain dain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment. Otherwise, Nezih, merge whenever you want.

ColumnIO[] fields = columnIO.getPath();
List<PrimitiveColumnIO> columns = getColumns(fileSchema, requestedSchema);
int index = -1;
for (int i = 0; i < columns.size(); i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename i and j to something more descriptive? Maybe columnIndex and level (and rename level to maxLevel)?

@dain dain assigned nezihyigitbasi and unassigned dain Apr 13, 2017
@zhenxiao zhenxiao force-pushed the parquet-tuple-domain branch from 5eda135 to ff5cd08 Compare April 13, 2017 00:57
@zhenxiao
Copy link
Collaborator Author

thank you @dain @nezihyigitbasi
get comments addressed

@nezihyigitbasi
Copy link
Contributor

thanks @zhenxiao I will merge this once the tests all pass.

@shurvitz
Copy link

Hi @zhenxiao ,
I have a question regarding this change to getDictionaries(). It looks like the change folded a loop inside a loop into a single loop. What I'm noticing in Presto217 is a decrease in bytes-scanned during the predicate push-down process. This is the end result of that loop hard-existing via break after the first predicate descriptor is matched and a dictionary page is added. Before the change, the loop would iterate over all descriptors... Was leaving the break statement intentional?

@zhenxiao
Copy link
Collaborator Author

Hi @shurvitz thanks for reaching out.
I did not quite get ur question, this PR is actually a refactoring, leveraging ColumnDescriptor in Parquet, instead of integer index, for dictionary pushdown and predicate pushdown.
Could you please elaborate more, with file line number?

@zhenxiao
Copy link
Collaborator Author

Get it. Thank you, @shurvitz @pettyjamesm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants