peter-toth commented on code in PR #59324:
URL: https://github.com/apache/spark/pull/59324#discussion_r4267720887
##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala:
##########
@@ -1976,37 +1969,20 @@ object SQLConf {
val PARQUET_STORAGE_FILTER_PUSHDOWN_ENABLED =
buildConf("spark.sql.parquet.storageFilterPushdown.enabled")
.doc("If true, the vectorized reader of the V1 Parquet file source may
apply a runtime " +
- "storage filter, such as a bloom filter from join runtime filtering,
while it reads. " +
- "It reads the columns the " +
- "filter needs first, evaluates the filter per row, and then reads the
remaining columns " +
- "only for the rows that survived. This is a planning-time decision. " +
- "A filter that is attached also stays in the post-scan filter, the way
a pushed data " +
- "filter does, so the reader is free to stop applying it wherever doing
so would cost " +
- "more than it saves, and the answer does not change. In a scan that
returns columnar " +
- "batches, the post-scan filter skips it on the rows the reader applied
it to, which the " +
- "reader marks in a column named _tmp_storage_filter_checked. A table
with a column of " +
- "that name is not offered storage filters, and a file holding one
outside the table's " +
- "schema is not supported. A row group where the reader stops applying
the filter reads " +
- "its key columns twice, which is slower than not pushing the filter. "
+
- "Under spark.sql.files.ignoreCorruptFiles the answer can change,
because this reader " +
- "reads different pages in a different order than a plain read. Which
rows survive a " +
- "corrupt page can then differ from a plain read, in either direction.
" +
- "Returning only the surviving rows of a row group relies on the
Parquet page index, so a " +
- "file whose page index is wrong can pair a row's key with another
row's values, or " +
- "return rows the filter rejects. Setting " +
- "parquet.filter.columnindex.enabled to false makes the reader fall
back to skipping " +
- "whole row groups in which the filter rejects every row. A row group
with a surviving " +
- "row then reads its key columns twice, and the post-scan filter
evaluates the filter " +
- "on each of its rows. A file without a page index falls back the same
" +
- "way. It gains only where a whole row group has no surviving row, a
bloom's false " +
- "positives included, and every other row group reads its key columns
twice. A read " +
- "with pushed data filters already relies on the page index, and that
conf turns it off " +
- "there too. A read with no pushed data filter relies on it only with
this feature. " +
- "Note that the surviving key values of a whole row group are buffered
before that row " +
- "group's first batch is produced. Each reader holds them and their row
ranges for one " +
- "row group at a time, up to " +
- s"${PARQUET_STORAGE_FILTER_PUSHDOWN_MAX_SPLICED_ROW_GROUP_BYTES.key}.
Like the reader's " +
- "own column vectors, they are not tracked by Spark's memory manager.")
+ "bloom filter from join runtime filtering while it reads, and skip the
pages and row " +
+ "groups of the other columns that hold no matching rows. Only
ParquetFileFormat itself " +
+ "is supported, not its subclasses. A table with a column named " +
+ "_tmp_storage_filter_checked is not offered storage filters, and a
file holding one " +
+ "outside the table's schema is not supported. The filter also stays in
the post-scan " +
+ "filter, so the answer does not change, with two exceptions. Under " +
+ "spark.sql.files.ignoreCorruptFiles the rows returned from a corrupt
file can differ. A " +
+ "file with a wrong Parquet page index can return wrong rows. Setting "
+
Review Comment:
Done in
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3):
"A file with a wrong Parquet page index can return wrong rows, even with no
pushed data filter."
##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala:
##########
@@ -1976,37 +1969,20 @@ object SQLConf {
val PARQUET_STORAGE_FILTER_PUSHDOWN_ENABLED =
buildConf("spark.sql.parquet.storageFilterPushdown.enabled")
.doc("If true, the vectorized reader of the V1 Parquet file source may
apply a runtime " +
- "storage filter, such as a bloom filter from join runtime filtering,
while it reads. " +
- "It reads the columns the " +
- "filter needs first, evaluates the filter per row, and then reads the
remaining columns " +
- "only for the rows that survived. This is a planning-time decision. " +
- "A filter that is attached also stays in the post-scan filter, the way
a pushed data " +
- "filter does, so the reader is free to stop applying it wherever doing
so would cost " +
- "more than it saves, and the answer does not change. In a scan that
returns columnar " +
- "batches, the post-scan filter skips it on the rows the reader applied
it to, which the " +
- "reader marks in a column named _tmp_storage_filter_checked. A table
with a column of " +
- "that name is not offered storage filters, and a file holding one
outside the table's " +
- "schema is not supported. A row group where the reader stops applying
the filter reads " +
- "its key columns twice, which is slower than not pushing the filter. "
+
- "Under spark.sql.files.ignoreCorruptFiles the answer can change,
because this reader " +
- "reads different pages in a different order than a plain read. Which
rows survive a " +
- "corrupt page can then differ from a plain read, in either direction.
" +
- "Returning only the surviving rows of a row group relies on the
Parquet page index, so a " +
- "file whose page index is wrong can pair a row's key with another
row's values, or " +
- "return rows the filter rejects. Setting " +
- "parquet.filter.columnindex.enabled to false makes the reader fall
back to skipping " +
- "whole row groups in which the filter rejects every row. A row group
with a surviving " +
- "row then reads its key columns twice, and the post-scan filter
evaluates the filter " +
- "on each of its rows. A file without a page index falls back the same
" +
- "way. It gains only where a whole row group has no surviving row, a
bloom's false " +
- "positives included, and every other row group reads its key columns
twice. A read " +
- "with pushed data filters already relies on the page index, and that
conf turns it off " +
- "there too. A read with no pushed data filter relies on it only with
this feature. " +
- "Note that the surviving key values of a whole row group are buffered
before that row " +
- "group's first batch is produced. Each reader holds them and their row
ranges for one " +
- "row group at a time, up to " +
- s"${PARQUET_STORAGE_FILTER_PUSHDOWN_MAX_SPLICED_ROW_GROUP_BYTES.key}.
Like the reader's " +
- "own column vectors, they are not tracked by Spark's memory manager.")
+ "bloom filter from join runtime filtering while it reads, and skip the
pages and row " +
+ "groups of the other columns that hold no matching rows. Only
ParquetFileFormat itself " +
+ "is supported, not its subclasses. A table with a column named " +
+ "_tmp_storage_filter_checked is not offered storage filters, and a
file holding one " +
+ "outside the table's schema is not supported. The filter also stays in
the post-scan " +
+ "filter, so the answer does not change, with two exceptions. Under " +
+ "spark.sql.files.ignoreCorruptFiles the rows returned from a corrupt
file can differ. A " +
+ "file with a wrong Parquet page index can return wrong rows. Setting "
+
+ "parquet.filter.columnindex.enabled to false stops relying on the page
index, and also " +
+ "turns off page skipping for pushed data filters. Without a page
index, as in the files " +
+ "pyarrow's write_table writes by default, or with that conf set to
false, the reader can " +
+ "only skip whole row groups, and it reads the key columns twice in the
others. It can be " +
Review Comment:
Done in
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3):
"With a page index, it can be slower when ...".
##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala:
##########
@@ -1976,37 +1969,20 @@ object SQLConf {
val PARQUET_STORAGE_FILTER_PUSHDOWN_ENABLED =
buildConf("spark.sql.parquet.storageFilterPushdown.enabled")
.doc("If true, the vectorized reader of the V1 Parquet file source may
apply a runtime " +
- "storage filter, such as a bloom filter from join runtime filtering,
while it reads. " +
- "It reads the columns the " +
- "filter needs first, evaluates the filter per row, and then reads the
remaining columns " +
- "only for the rows that survived. This is a planning-time decision. " +
- "A filter that is attached also stays in the post-scan filter, the way
a pushed data " +
- "filter does, so the reader is free to stop applying it wherever doing
so would cost " +
- "more than it saves, and the answer does not change. In a scan that
returns columnar " +
- "batches, the post-scan filter skips it on the rows the reader applied
it to, which the " +
- "reader marks in a column named _tmp_storage_filter_checked. A table
with a column of " +
- "that name is not offered storage filters, and a file holding one
outside the table's " +
- "schema is not supported. A row group where the reader stops applying
the filter reads " +
- "its key columns twice, which is slower than not pushing the filter. "
+
- "Under spark.sql.files.ignoreCorruptFiles the answer can change,
because this reader " +
- "reads different pages in a different order than a plain read. Which
rows survive a " +
- "corrupt page can then differ from a plain read, in either direction.
" +
- "Returning only the surviving rows of a row group relies on the
Parquet page index, so a " +
- "file whose page index is wrong can pair a row's key with another
row's values, or " +
- "return rows the filter rejects. Setting " +
- "parquet.filter.columnindex.enabled to false makes the reader fall
back to skipping " +
- "whole row groups in which the filter rejects every row. A row group
with a surviving " +
- "row then reads its key columns twice, and the post-scan filter
evaluates the filter " +
- "on each of its rows. A file without a page index falls back the same
" +
- "way. It gains only where a whole row group has no surviving row, a
bloom's false " +
- "positives included, and every other row group reads its key columns
twice. A read " +
- "with pushed data filters already relies on the page index, and that
conf turns it off " +
- "there too. A read with no pushed data filter relies on it only with
this feature. " +
- "Note that the surviving key values of a whole row group are buffered
before that row " +
- "group's first batch is produced. Each reader holds them and their row
ranges for one " +
- "row group at a time, up to " +
- s"${PARQUET_STORAGE_FILTER_PUSHDOWN_MAX_SPLICED_ROW_GROUP_BYTES.key}.
Like the reader's " +
- "own column vectors, they are not tracked by Spark's memory manager.")
+ "bloom filter from join runtime filtering while it reads, and skip the
pages and row " +
+ "groups of the other columns that hold no matching rows. Only
ParquetFileFormat itself " +
+ "is supported, not its subclasses. A table with a column named " +
+ "_tmp_storage_filter_checked is not offered storage filters, and a
file holding one " +
+ "outside the table's schema is not supported. The filter also stays in
the post-scan " +
+ "filter, so the answer does not change, with two exceptions. Under " +
+ "spark.sql.files.ignoreCorruptFiles the rows returned from a corrupt
file can differ. A " +
+ "file with a wrong Parquet page index can return wrong rows. Setting "
+
+ "parquet.filter.columnindex.enabled to false stops relying on the page
index, and also " +
+ "turns off page skipping for pushed data filters. Without a page
index, as in the files " +
+ "pyarrow's write_table writes by default, or with that conf set to
false, the reader can " +
+ "only skip whole row groups, and it reads the key columns twice in the
others. It can be " +
+ "slower when the matching rows are spread across most pages. The
matching key values of " +
+ "a row group are buffered while it is read, outside Spark's memory
manager.")
Review Comment:
Done in
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3),
with the bound as viirya's text had it: "..., up to
spark.sql.parquet.storageFilterPushdown.maxSplicedRowGroupBytes, outside
Spark's memory manager."
##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala:
##########
@@ -1976,37 +1969,20 @@ object SQLConf {
val PARQUET_STORAGE_FILTER_PUSHDOWN_ENABLED =
buildConf("spark.sql.parquet.storageFilterPushdown.enabled")
.doc("If true, the vectorized reader of the V1 Parquet file source may
apply a runtime " +
- "storage filter, such as a bloom filter from join runtime filtering,
while it reads. " +
- "It reads the columns the " +
- "filter needs first, evaluates the filter per row, and then reads the
remaining columns " +
- "only for the rows that survived. This is a planning-time decision. " +
- "A filter that is attached also stays in the post-scan filter, the way
a pushed data " +
- "filter does, so the reader is free to stop applying it wherever doing
so would cost " +
- "more than it saves, and the answer does not change. In a scan that
returns columnar " +
- "batches, the post-scan filter skips it on the rows the reader applied
it to, which the " +
- "reader marks in a column named _tmp_storage_filter_checked. A table
with a column of " +
- "that name is not offered storage filters, and a file holding one
outside the table's " +
- "schema is not supported. A row group where the reader stops applying
the filter reads " +
- "its key columns twice, which is slower than not pushing the filter. "
+
- "Under spark.sql.files.ignoreCorruptFiles the answer can change,
because this reader " +
- "reads different pages in a different order than a plain read. Which
rows survive a " +
- "corrupt page can then differ from a plain read, in either direction.
" +
- "Returning only the surviving rows of a row group relies on the
Parquet page index, so a " +
- "file whose page index is wrong can pair a row's key with another
row's values, or " +
- "return rows the filter rejects. Setting " +
- "parquet.filter.columnindex.enabled to false makes the reader fall
back to skipping " +
- "whole row groups in which the filter rejects every row. A row group
with a surviving " +
- "row then reads its key columns twice, and the post-scan filter
evaluates the filter " +
- "on each of its rows. A file without a page index falls back the same
" +
- "way. It gains only where a whole row group has no surviving row, a
bloom's false " +
- "positives included, and every other row group reads its key columns
twice. A read " +
- "with pushed data filters already relies on the page index, and that
conf turns it off " +
- "there too. A read with no pushed data filter relies on it only with
this feature. " +
- "Note that the surviving key values of a whole row group are buffered
before that row " +
- "group's first batch is produced. Each reader holds them and their row
ranges for one " +
- "row group at a time, up to " +
- s"${PARQUET_STORAGE_FILTER_PUSHDOWN_MAX_SPLICED_ROW_GROUP_BYTES.key}.
Like the reader's " +
- "own column vectors, they are not tracked by Spark's memory manager.")
+ "bloom filter from join runtime filtering while it reads, and skip the
pages and row " +
+ "groups of the other columns that hold no matching rows. Only
ParquetFileFormat itself " +
+ "is supported, not its subclasses. A table with a column named " +
+ "_tmp_storage_filter_checked is not offered storage filters, and a
file holding one " +
+ "outside the table's schema is not supported. The filter also stays in
the post-scan " +
+ "filter, so the answer does not change, with two exceptions. Under " +
+ "spark.sql.files.ignoreCorruptFiles the rows returned from a corrupt
file can differ. A " +
+ "file with a wrong Parquet page index can return wrong rows. Setting "
+
+ "parquet.filter.columnindex.enabled to false stops relying on the page
index, and also " +
Review Comment:
I'd leave this one. It is parquet's Hadoop key, and where such a key can be
set is the same for every one of them, so it is not this conf's to explain.
##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/ParquetBlockAccounting.java:
##########
@@ -84,29 +77,14 @@ boolean hasOffsetIndexes(int blockIndex, Set<ColumnPath>
paths) {
}
/**
- * Compressed bytes a reader transfers for the leaf columns named by {@code
paths} when it reads
- * exactly {@code rowRanges} of the given block. Page headers and the
dictionary page are
- * included, since both are read whenever any page of a chunk is read.
{@code rowRangeCount} is
- * {@code rowRanges.rowCount()}, passed in because that walks every range
and the caller has it.
- *
- * <p>Two sources, chosen so this never causes IO of its own:
- * <ul>
- * <li>{@code rowRanges} covers the whole block, and the answer is the sum
of the chunks'
- * {@code getTotalSize()}, which is already in the footer. This is the
case that matters.
- * Whenever nothing else has built the block's {@link
ColumnIndexStore}, {@code rowRanges}
- * is necessarily the whole block, because a narrower range can only
come from column-index
- * filtering, which builds the store as a side effect.
- * <li>{@code rowRanges} is a strict subset, and the walk goes over the
offset index, as
- * parquet's own read path does, adding the dictionary page the way
- * {@code calculateOffsetRanges} does. The store is guaranteed to
exist here, so the walk is
- * pure metadata arithmetic. For ranges a reader narrowed itself,
which column-index
- * filtering had no hand in, that guarantee is an ordering one, since
the reader's own read
- * of those ranges built the store first. A new caller has to keep
that order.
- * </ul>
+ * Compressed bytes a reader transfers for the columns in {@code paths} when
it reads exactly
+ * {@code rowRanges} of the block, page headers and the dictionary page
included.
+ * {@code rowRangeCount} is {@code rowRanges.rowCount()}, which the caller
has.
*
- * <p>Columns absent from this physical file (schema evolution) contribute
nothing, which is
- * correct, since the reader transfers nothing for them. They fall out of
the walk by themselves,
- * which goes over the block's chunks rather than over {@code paths}.
+ * <p>A whole block is the chunks' sizes from the footer. A strict subset is
walked over the
+ * offset index, as parquet's read does. That reads the block's {@link
ColumnIndexStore}, which
+ * costs no IO only once a read of those ranges has built it, so a new
caller has to keep that
Review Comment:
Done in
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3).
It now says which reads build the store, and that a walk before them breaks a
later read of other columns.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]