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 page filtering on min_max for decimal128 and string columns #4255

Merged
merged 8 commits into from
Nov 22, 2022

Conversation

Ted-Jiang
Copy link
Member

@Ted-Jiang Ted-Jiang commented Nov 17, 2022

Which issue does this PR close?

Related #3833.

Rationale for this change

Support page index filter on min_max for type decimal and string.

The string code is from @alamb Thanks!

Only cast to Decimal128Array align with row_group prunning.

As null_count, i prefer fix in next pr.

What changes are included in this PR?

Are these changes tested?

Are there any user-facing changes?

@github-actions github-actions bot added the core Core DataFusion crate label Nov 17, 2022
// Convert the bytes array to i128.
// The endian of the input bytes array must be big-endian.
// Copy from the arrow-rs
pub(crate) fn from_bytes_to_i128(b: &[u8]) -> i128 {
Copy link
Member Author

Choose a reason for hiding this comment

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

Move the common func here.

Copy link
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

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

Thank you @Ted-Jiang -- this looks great

I wonder if it would be possible to add some more targeted testing for the string and decimal page indexes in https://github.com/apache/arrow-datafusion/blob/master/datafusion/core/tests/parquet/page_pruning.rs

The current test in parquet_exec I think ensures that the plumbing is all hooked up correctly, but I think some more targeted testing would be good too

However, overall I think this PR could also go in as is. Thanks a lot!

@@ -266,20 +266,17 @@ async fn single_file_small_data_pages() {
// page 3: DLE:RLE RLE:RLE VLE:RLE_DICTIONARY ST:[min: djzdyiecnumrsrcbizwlqzdhnpoiqdh, max: fktdcgtmzvoedpwhfevcvvrtaurzgex, num_nulls not defined] CRC:[none] SZ:7 VC:9216
// page 4: DLE:RLE RLE:RLE VLE:RLE_DICTIONARY ST:[min: fktdcgtmzvoedpwhfevcvvrtaurzgex, max: fwtdpgtxwqkkgtgvthhwycrvjiizdifyp, num_nulls not defined] CRC:[none] SZ:7 VC:9216
// page 5: DLE:RLE RLE:RLE VLE:RLE_DICTIONARY ST:[min: fwtdpgtxwqkkgtgvthhwycrvjiizdifyp, max: iadnalqpdzthpifrvewossmpqibgtsuin, num_nulls not defined] CRC:[none] SZ:7 VC:7739
//
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

@@ -146,6 +148,7 @@ impl BatchBuilder {
.append_option(rng.gen_bool(0.9).then(|| rng.gen()));
self.response_status
.append_value(status[rng.gen_range(0..status.len())]);
self.prices_status.append_value(self.row_count as i128);
Copy link
Contributor

Choose a reason for hiding this comment

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

the incrementing price makes sense for range testing

@@ -382,6 +394,9 @@ fn create_row_count_in_each_page(
struct PagesPruningStatistics<'a> {
col_page_indexes: &'a Index,
col_offset_indexes: &'a Vec<PageLocation>,
// target_type means the logical type in schema: like 'DECIMAL' is the logical type, but the
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@@ -419,10 +468,37 @@ macro_rules! get_min_max_values_for_page_index {
vec.iter().map(|x| x.$func().cloned()),
)))
}
Index::INT96(_) | Index::BYTE_ARRAY(_) | Index::FIXED_LEN_BYTE_ARRAY(_) => {
Index::BYTE_ARRAY(index) => {
let vec = &index.indexes;
Copy link
Contributor

Choose a reason for hiding this comment

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

decimal should be supported for this logical type.

Copy link
Contributor

Choose a reason for hiding this comment

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

Arrow-rs contains the method of decoding decimal from byte array in ByteArrayReader

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, i prefer align with row group, do them together in other pr.

Copy link
Contributor

Choose a reason for hiding this comment

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

Addition additional support in a follow on PR sounds like a good idea to me -- maybe we can file a ticket to track the work

@liukun4515
Copy link
Contributor

Thank you @Ted-Jiang -- this looks great

I wonder if it would be possible to add some more targeted testing for the string and decimal page indexes in https://github.com/apache/arrow-datafusion/blob/master/datafusion/core/tests/parquet/page_pruning.rs

The current test in parquet_exec I think ensures that the plumbing is all hooked up correctly, but I think some more targeted testing would be good too

However, overall I think this PR could also go in as is. Thanks a lot!

agree.

@Ted-Jiang
We can add more test for this, and use different physical data type with different decimal data type(diff precision and scale are better)

Signed-off-by: yangjiang <[email protected]>
@Ted-Jiang
Copy link
Member Author

will add more test

Signed-off-by: yangjiang <[email protected]>
Signed-off-by: yangjiang <[email protected]>
@Ted-Jiang
Copy link
Member Author

Ted-Jiang commented Nov 18, 2022

@alamb @liukun4515 Add types check same as in row_group for page-index pruning.

reorg test code: code refactoring avoid duplicate code in test.
add test for page index: add same test case for page index.

Some test are ignore, i think there are some bug with complex_expr 🤔 will fix in next pr(not have a clue now)

@@ -503,465 +483,3 @@ async fn prune_decimal_in_list() {
)
.await;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

code move to mod.rs

}

#[tokio::test]
#[ignore]
Copy link
Member Author

Choose a reason for hiding this comment

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

All test case with expr fail 😭

Copy link
Contributor

@alamb alamb Nov 21, 2022

Choose a reason for hiding this comment

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

I wonder if we have to run "type coercion / simplifiction" on them first?

Did rowGroup run this "type coercion / simplifiction" 🤔 ? I think they are the same code path, i will find it out soon.

Copy link
Contributor

Choose a reason for hiding this comment

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

@Ted-Jiang Does the test_prune function not run the optimizer?

Copy link
Member Author

@Ted-Jiang Ted-Jiang Nov 22, 2022

Choose a reason for hiding this comment

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

File related #4317

@alamb
Copy link
Contributor

alamb commented Nov 20, 2022

I plan to review this carefully tomorrow again --sorry for the delay @Ted-Jiang

Copy link
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

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

I think this is looking good @Ted-Jiang -- the only thing I think that should be addressed before merging is the use of min() rather than$func() -- and that may just be my misunderstanding.

Once that is sorted out, perhaps we then get this PR in and then iterate on additional changes / data type support as follow ons?

let vec = &index.indexes;
let vec: Vec<Option<i128>> = vec
.iter()
.map(|x| x.min().and_then(|x| Some(*x as i128)))
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this this be $x.$func() rather than x.min()?

Copy link
Member Author

Choose a reason for hiding this comment

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

Your are right! i forgot change it writing the macro, real surprise ut not cover this 😂

Copy link
Member Author

Choose a reason for hiding this comment

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

i will add a greater than test case.

let vec = &index.indexes;
let vec: Vec<Option<i128>> = vec
.iter()
.map(|x| x.min().and_then(|x| Some(*x as i128)))
Copy link
Contributor

Choose a reason for hiding this comment

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

same question here -- should this be x.$func() rather than x.min()?

Comment on lines 416 to 422
if let Ok(arr) = Decimal128Array::from(vec)
.with_precision_and_scale(*precision, *scale)
{
return Some(Arc::new(arr));
} else {
return None;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You might be able to this more functionally with something like (untested):

Suggested change
if let Ok(arr) = Decimal128Array::from(vec)
.with_precision_and_scale(*precision, *scale)
{
return Some(Arc::new(arr));
} else {
return None;
}
Decimal128Array::from(vec)
.with_precision_and_scale(*precision, *scale)
.ok()
.map(|arr| Arc::new(arr))

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice suggestion ! Some much api need remember deal with option and result 😂

@@ -419,10 +468,37 @@ macro_rules! get_min_max_values_for_page_index {
vec.iter().map(|x| x.$func().cloned()),
)))
}
Index::INT96(_) | Index::BYTE_ARRAY(_) | Index::FIXED_LEN_BYTE_ARRAY(_) => {
Index::BYTE_ARRAY(index) => {
let vec = &index.indexes;
Copy link
Contributor

Choose a reason for hiding this comment

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

Addition additional support in a follow on PR sounds like a good idea to me -- maybe we can file a ticket to track the work

@@ -204,3 +222,466 @@ async fn page_index_filter_multi_col() {
let batch = results.next().await.unwrap().unwrap();
assert_eq!(batch.num_rows(), 7300);
}

async fn test_prune(
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great coverage -- thanks @Ted-Jiang. It is somewhat repetitive with the row group pruning but I think that is ok as they are different code paths

}

#[tokio::test]
#[ignore]
Copy link
Contributor

@alamb alamb Nov 21, 2022

Choose a reason for hiding this comment

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

I wonder if we have to run "type coercion / simplifiction" on them first?

Did rowGroup run this "type coercion / simplifiction" 🤔 ? I think they are the same code path, i will find it out soon.

}

#[tokio::test]
async fn prune_decimal_eq() {
Copy link
Contributor

Choose a reason for hiding this comment

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

it might be worth another test that prunes something other than 5 rows -- maybe where decimal_col = 30.00 and prunes out the other pages? All of the tests here seem to prune out only the third page 20.00 -> 60.00

@xudong963
Copy link
Member

xudong963 commented Nov 22, 2022

I'll take a look later :)

Signed-off-by: yangjiang <[email protected]>
Signed-off-by: yangjiang <[email protected]>
@alamb
Copy link
Contributor

alamb commented Nov 22, 2022

I think this is ready to go in now -- thank you @xudong963 and @Ted-Jiang and @liukun4515

@alamb alamb merged commit d7a7fb6 into apache:master Nov 22, 2022
@ursabot
Copy link

ursabot commented Nov 22, 2022

Benchmark runs are scheduled for baseline = eac254c and contender = d7a7fb6. d7a7fb6 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Skipped ⚠️ Benchmarking of arrow-datafusion-commits is not supported on ec2-t3-xlarge-us-east-2] ec2-t3-xlarge-us-east-2
[Skipped ⚠️ Benchmarking of arrow-datafusion-commits is not supported on test-mac-arm] test-mac-arm
[Skipped ⚠️ Benchmarking of arrow-datafusion-commits is not supported on ursa-i9-9960x] ursa-i9-9960x
[Skipped ⚠️ Benchmarking of arrow-datafusion-commits is not supported on ursa-thinkcentre-m75q] ursa-thinkcentre-m75q
Buildkite builds:
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

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

Successfully merging this pull request may close these issues.

5 participants