av9ash/GitBugs huggingface dataset:
Bug: Fix an error in BenchmarkBase.scala that occurs when creating a benchmark file in a non-existent directory
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 29/Apr/21 04:32
Affects Version/s: 3.2.0

When submitting a benchmark job using _org.apache.spark.benchmark.Benchmarks_ class with _SPARK_GENERATE_BENCHMARK_FILES=1_ option, an exception is raised if the directory where the benchmark file will be generated does not exist.
 For example, if you execute _BLASBenchmark_ like the command below, you get an error unless you manually create _benchmarks/_ directory under _spark/mllib-local/_.
{code:java}
SPARK_GENERATE_BENCHMARK_FILES=1 bin/spark-submit \
--driver-memory 6g --class org.apache.spark.benchmark.Benchmarks \
--jars "`find . -name '*-SNAPSHOT-tests.jar' -o -name '*avro*-SNAPSHOT.jar' | paste -sd ',' -`" \
"`find . -name 'spark-core*-SNAPSHOT-tests.jar'`" \
"org.apache.spark.ml.linalg.BLASBenchmark"
{code}
This is caused by the code in _BenchmarkBase.scala_ where an attempt is made to create the benchmark file without validating the path.
Bug: Extract a new method to eliminate duplicate code in `BufferReleasingInputStream`
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 12/Apr/21 12:52
Affects Version/s: 3.2.0

There are some duplicate code patterns in `BufferReleasingInputStream`, such as 

 
{code:java}
override def read(): Int = { 
  try { 
    delegate.read() 
  } catch { 
    case e: IOException if detectCorruption => 
      IOUtils.closeQuietly(this) 
      iterator.throwFetchFailedException(blockId, mapIndex, address, e) 
  } 
}
{code}
, 

 
{code:java}
override def read(b: Array[Byte]): Int = {    
  try {      
    delegate.read(b)    
  } catch {      
    case e: IOException if detectCorruption =>        
      IOUtils.closeQuietly(this)        
      iterator.throwFetchFailedException(blockId, mapIndex, address, e)    
  }  
}
{code}
 

and

 
{code:java}
override def read(b: Array[Byte], off: Int, len: Int): Int = {    
  try {      
    delegate.read(b, off, len)    
  } catch {      
    case e: IOException if detectCorruption =>        
      IOUtils.closeQuietly(this)        
      iterator.throwFetchFailedException(blockId, mapIndex, address, e)    
  }  
}
{code}
Bug: Spark allows to set spark.driver.cores=0
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 09/Apr/21 18:39
Affects Version/s: 2.4.7

I found an inconsistency in [validation logic of Spark submit arguments |https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala#L248-L258]that allows *spark.driver.cores* value to be set to 0 but requires *spark.driver.memory,* *spark.executor.cores, spark.executor.memory* to be positive numbers:
{quote}Exception in thread "main" org.apache.spark.SparkException: Driver memory must be a positive number
 Exception in thread "main" org.apache.spark.SparkException: Executor cores must be a positive number
 Exception in thread "main" org.apache.spark.SparkException: Executor memory must be a positive number
{quote}
I would like to understand if there is a reason for this inconsistency in the validation logic or it is a bug?

Thank you
Bug: MapStatusesSerDeserBenchmark causes a side effect to other benchmarks with tasks being too big (JDK 11)
Status: Resolved | Priority: Minor | Resolution: Invalid
Created: 01/Apr/21 02:53
Affects Version/s: 3.2.0

In JDK 11, MapStatusesSerDeserBenchmark (being started failed) seems affecting other benchmark cases with growing the size of task:

{code}
2021-03-31T16:46:43.1179145Z 21/03/31 16:46:43 WARN DAGScheduler: Broadcasting large task binary with size 42.2 MiB
2021-03-31T16:46:47.3079315Z 21/03/31 16:46:47 WARN DAGScheduler: Broadcasting large task binary with size 42.2 MiB
2021-03-31T16:46:51.5920733Z 21/03/31 16:46:51 WARN DAGScheduler: Broadcasting large task binary with size 42.2 MiB
2021-03-31T16:46:55.9175194Z 21/03/31 16:46:55 WARN DAGScheduler: Broadcasting large task binary with size 42.2 MiB
2021-03-31T16:46:57.6874541Z   Stopped after 3 iterations, 12928 ms
2021-03-31T16:46:57.6875644Z 
2021-03-31T16:46:57.6877153Z OpenJDK 64-Bit Server VM 11.0.10+9-LTS on Linux 5.4.0-1041-azure
2021-03-31T16:46:57.7095280Z Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz
2021-03-31T16:46:57.7097654Z from_json as subExpr in Project:          Best Time(ms)   Avg Time(ms)   Stdev(ms)    Rate(M/s)   Per Row(ns)   Relative
2021-03-31T16:46:57.7099059Z ------------------------------------------------------------------------------------------------------------------------
2021-03-31T16:46:57.7100274Z subExprElimination false, codegen: true           38880          41246        1389          0.0   388800445.2       1.0X
2021-03-31T16:46:57.7101134Z subExprElimination false, codegen: false          35819          38141        1234          0.0   358188088.6       1.1X
2021-03-31T16:46:57.7106264Z subExprElimination true, codegen: true             3947           4157         364          0.0    39465629.1       9.9X
2021-03-31T16:46:57.7106982Z subExprElimination true, codegen: false            4191           4309         112          0.0    41908945.5       9.3X
2021-03-31T16:46:57.7107595Z 
2021-03-31T16:46:57.7135178Z Preparing data for benchmarking ...
2021-03-31T16:46:58.5630584Z Running benchmark: from_json as subExpr in Filter
2021-03-31T16:46:58.5633083Z   Running case: subExprElimination false, codegen: true
2021-03-31T16:48:25.5619312Z 21/03/31 16:48:25 WARN DAGScheduler: Broadcasting large task binary with size 43.0 MiB
{code}

It only happens when the benchmarks run sequentially via Benchmarks.scala.
Bug: Add an internal option to control input buffer in univocity
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 13/Apr/21 06:19
Affects Version/s: 3.2.0

https://github.com/apache/spark/pull/31858 fixed it to respect Univocity's default buffer to:
- Firstly, it's best to trust their judgement on the default values. Also 128 is too low.
- Default values arguably have more test coverage in Univocity.
- It will also fix uniVocity/univocity-parsers#449 which is a regression compared to Spark 2.4

To prevent related side effect, we should have a workaround to change the buffer size.
Bug: AQE improve: change shuffle hash join to sort merge join when skewed shuffle hash join exists
Status: Resolved | Priority: Minor | Resolution: Not A Problem
Created: 08/Apr/21 06:41
Affects Version/s: 3.0.1

In our production, `spark.sql.join.preferSortMergeJoin` is false by default. AQE currently can only optimize skewed join for sort merge join, it will be better if we can change shuffle hash join to sort merge join when skewed shuffle hash join exists.
Bug: Port minimal TPC-DS datagen code from databricks/spark-sql-perf
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 22/Apr/21 13:12
Affects Version/s: 3.2.0

This PR aims at porting minimal code to generate TPC-DS data from databricks/spark-sql-perf. The classes in a new class file tpcdsDatagen.scala are basically copied from the databricks/spark-sql-perf codebase.

We frequently use TPCDS data now for benchmarks/tests, but the classes for the TPCDS schemas of datagen and benchmarks/tests are managed separately, e.g.,

https://github.com/apache/spark/blob/master/sql/core/src/test/scala/org/apache/spark/sql/TPCDSBase.scala
https://github.com/databricks/spark-sql-perf/blob/master/src/main/scala/com/databricks/spark/sql/perf/tpcds/TPCDSTables.scala
I think this causes some inconveniences, e.g., we need to update both files in the separate repositories if we update the TPCDS schema #32037. So, it would be useful for the Spark codebase to generate them by referring to the same schema definition.
Bug: Aggregate (Min/Max/Count) push down for ORC
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 05/Apr/21 20:00
Affects Version/s: 3.2.0

Similar to Parquet (https://issues.apache.org/jira/browse/SPARK-34952), we can also push down certain aggregations into ORC. ORC exposes column statistics in interface `org.apache.orc.Reader` ([https://github.com/apache/orc/blob/master/java/core/src/java/org/apache/orc/Reader.java#L118] ), where Spark can utilize for aggregation push down.
Bug: Support coalesce partition through union
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 07/Apr/21 09:53
Affects Version/s: 3.2.0

The rule `CoalesceShufflePartitions` can only coalesce paritition if
* leaf node is ShuffleQueryStage
* all shuffle have same partition number

With `Union`, it might break the assumption. Let's say we have such plan
{code:java}
Union
   HashAggregate
      ShuffleQueryStage
   FileScan
{code}

`CoalesceShufflePartitions` can not optimize it and the result partition would be `shuffle partition + FileScan partition` which can be quite lagre.

It's better to support partial optimize with `Union`.
Bug: Remove the description that || and && can be used as logical operators from the document.
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 01/Apr/21 08:27
Affects Version/s: 2.4.7

At the Compatibility with Apache Hive section in the migration guide, it describes that || and && can be used as logical operators.
But, in fact, they cannot be used as described.
AFAIK, Hive also doesn't support && and || as logical operators.
Bug: Support two level map for final hash aggregation
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 19/Apr/21 23:29
Affects Version/s: 3.2.0

For partial hash aggregation (code-gen path), we have two level of hash map for aggregation. First level is from `RowBasedHashMapGenerator`, which is computation faster compared to the second level from `UnsafeFixedWidthAggregationMap`. The introducing of two level hash map can help improve CPU performance of query as the first level hash map normally fits in hardware cache and has cheaper hash function for key lookup.

For final hash aggregation, we can also support two level of hash map, to improve query performance further.
Bug: Avoid opening watch when waitAppCompletion is false
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 21/Apr/21 10:37
Affects Version/s: 3.1.1

In spark-submit, we currently [open a pod watch for any spark submission|https://github.com/apache/spark/blame/0494dc90af48ce7da0625485a4dc6917a244d580/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/KubernetesClientApplication.scala#L150-L167]. If WAIT_FOR_APP_COMPLETION is false, we then immediately ignore the result of the watcher and break out of the watcher.

When submitting spark applications at scale, this is a source of operational pain, since opening the watch relies on opening a websocket, which tends to run into subtle networking issues around negotiating the websocket connection.

I'd like to change this behaviour so that we eagerly check whether we are waiting on app completion, and avoid opening the watch altogether when WAIT_FOR_APP_COMPLETION is false.

Would you accept a contribution for that change, or are there any concerns I've overlooked?
Bug: Suppress `symbol literal is deprecated` compilation warnings in Scala 2.13
Status: Resolved | Priority: Minor | Resolution: Duplicate
Created: 20/Apr/21 14:40
Affects Version/s: 3.2.0

Add compile args to suppress  compilation warnings  as follows:

 
{code:java}
[warn] /home/kou/work/oss/spark-scala-2.13/examples/src/main/scala/org/apache/spark/examples/sql/SimpleTypedAggregator.scala:34:38: [deprecation @  | origin= | version=2.13.0] symbol literal is deprecated; use Symbol("id") instead
[warn]     val ds = spark.range(20).select(('id % 3).as("key"), 'id).as[(Long, Long)]
[warn]                                      ^
[warn] /home/kou/work/oss/spark-scala-2.13/examples/src/main/scala/org/apache/spark/examples/sql/SimpleTypedAggregator.scala:34:58: [deprecation @  | origin= | version=2.13.0] symbol literal is deprecated; use Symbol("id") instead
[warn]     val ds = spark.range(20).select(('id % 3).as("key"), 'id).as[(Long, Long)]
{code}
Bug: The view with udf created by hive1.x cannot be read by spark
Status: Open | Priority: Minor | Resolution: 
Created: 06/Apr/21 15:10
Affects Version/s: 2.4.0

First , use the command to register a function in hive:
{code:java}
create function shezm.hello as 'test.Hello' using jar 'hdfs:///udf_test/udf_test.jar'
{code}
 

 Then create view with the udf in hive1.1 , like  
{code:java}
create view shezm.test_view AS select shezm.hello(name) as v from shezm.test;
{code}
and read it use spark , it will get an error :
{code:java}
Exception in thread "main" org.apache.spark.sql.AnalysisException: Undefined function: 'shezm.hello'. This function is neither a registered temporary function nor a permanent function registered in the database 'default'.; line 1 pos 7
at org.apache.spark.sql.catalyst.analysis.Analyzer$LookupFunctions$$anonfun$apply$15$$anonfun$applyOrElse$51.apply(Analyzer.scala:1355) at org.apache.spark.sql.catalyst.analysis.Analyzer$LookupFunctions$$anonfun$apply$15$$anonfun$applyOrElse$51.apply(Analyzer.scala:1355) at org.apache.spark.sql.catalyst.analysis.Analyzer$LookupFunctions$$anonfun$apply$15$$anonfun$applyOrElse$51.apply(Analyzer.scala:1355) at org.apache.spark.sql.catalyst.analysis.package$.withPosition(package.scala:53) at org.apache.spark.sql.catalyst.analysis.Analyzer$LookupFunctions$$anonfun$apply$15.applyOrElse(Analyzer.scala:1354) at org.apache.spark.sql.catalyst.analysis.Analyzer$LookupFunctions$$anonfun$apply$15.applyOrElse(Analyzer.scala:1346) at org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$2.apply(TreeNode.scala:256) at org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$2.apply(TreeNode.scala:256)
......
{code}
 

When I investigated this issue, I found hive1.x will wrap all udf with backticks when create view with udf .like this:
{code:java}
hive> use shezm;
OK
Time taken: 0.999 seconds
hive> show create table test_view;
OK
CREATE VIEW `test_view` AS select `shezm.hello`(`test`.`id`) from `shezm`.`test`
Time taken: 1.761 seconds, Fetched: 1 row(s)

{code}
Spark will treat `shezm.hello` as a udf name, and cannot parse out the database (hive can).

I read the SqlBase.g4 file, the characters wrapped in backticks will be treated as complete strings, which seems to be a feature.

 

So, maybe this problem should be solved in AstBuilder#visitFunctionName()? By adding a case?
Bug: Close the inputStream in FileAppender when writing the logs failure
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 12/Apr/21 08:47
Affects Version/s: 3.1.1

In Spark Cluster, the ExecutorRunner uses FileAppender  to redirect the stdout/stderr of executors to file, when the writing processing is failure due to some reasons: disk full, the FileAppender will only close the input stream to file, but leave the pipe's stdout/stderr open, following writting operation in executor side may be hung. 

need to close the inputStream in FileAppender ?
Bug: Test case for function COALESCE() does not cover the CodeGen support
Status: Resolved | Priority: Minor | Resolution: Won't Fix
Created: 14/Apr/21 07:28
Affects Version/s: 3.0.1

The test case in org.apache.spark.sql.SQLQuerySuite named "Add Parser of SQL COALESCE" does not cover the codegen support for function COALESCE.
Bug: _SUCCESS file not written when using partitionOverwriteMode=dynamic
Status: In Progress | Priority: Minor | Resolution: 
Created: 30/Apr/21 03:59
Affects Version/s: 3.1.1

Steps to reproduce:

 
{code:java}
case class A(a: String, b:String)
def df = List(A("a", "b")).toDF
spark.conf.set("spark.sql.sources.partitionOverwriteMode", "dynamic")
val writer = df.write.mode(SaveMode.Overwrite).partitionBy("a") 
writer.parquet("s3a://some_bucket/test/")
{code}

when spark.sql.sources.partitionOverwriteMode is set to dynamic, the output written doesn't have _SUCCESS file updated.

(I have checked different versions of hadoop from 3.1.4 to 3.22 and they all behave the same, so the issue is with spark)

This is working in spark 3.0.2
Bug: Upgrade GenJavadoc to 0.17
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 29/Apr/21 06:07
Affects Version/s: 3.2.0

GenJavadoc 0.17 was released and it seems to include a fix for an issue which can happen with Scala 2.13.5.
https://github.com/lightbend/genjavadoc/releases/tag/v0.17
Bug: Subexpression elimination leading to a performance regression
Status: Open | Priority: Minor | Resolution: 
Created: 28/Apr/21 14:07
Affects Version/s: 3.1.1

I'm seeing almost double the runtime between 3.0.1 and 3.1.1 in my pipeline that does mostly str_to_map, split and a few other operations - all projections, no joins or aggregations (it's here only to trigger the pipeline). I cut it down to the simplest reproducible example I could - anything I remove from this changes the runtime difference quite dramatically. (even moving those two expressions from f.when to standalone columns makes the difference disappear)
{code:java}
import time
import os

import pyspark  
from pyspark.sql import SparkSession

import pyspark.sql.functions as f

if __name__ == '__main__':
    print(pyspark.__version__)
    spark = SparkSession.builder.getOrCreate()

    filename = 'regression.csv'
    if not os.path.isfile(filename):
        with open(filename, 'wt') as fw:
            fw.write('foo\n')
            for _ in range(10_000_000):
                fw.write('foo=bar&baz=bak&bar=f,o,1:2:3\n')

    df = spark.read.option('header', True).csv(filename)
    t = time.time()
    dd = (df
            .withColumn('my_map', f.expr('str_to_map(foo, "&", "=")'))
            .withColumn('extracted',
                            # without this top level split it is only 50% slower, with it
                            # the runtime almost doubles
                            f.split(f.split(f.col("my_map")["bar"], ",")[2], ":")[0]
                       )
            .select(
                f.when(
                    f.col("extracted").startswith("foo"), f.col("extracted")
                ).otherwise(
                    f.concat(f.lit("foo"), f.col("extracted"))
                ).alias("foo")
            )
        )
    # dd.explain(True)
    _ = dd.groupby("foo").count().count()
    print("elapsed", time.time() - t)
{code}
Running this in 3.0.1 and 3.1.1 respectively (both installed from PyPI, on my local macOS)
{code:java}
3.0.1
elapsed 21.262351036071777
3.1.1
elapsed 40.26582884788513
{code}
(Meaning the transformation took 21 seconds in 3.0.1 and 40 seconds in 3.1.1)

Feel free to make the CSV smaller to get a quicker feedback loop - it scales linearly (I developed this with 2M rows).

It might be related to my previous issue - SPARK-32989 - there are similar operations, nesting etc. (splitting on the original column, not on a map, makes the difference disappear)

I tried dissecting the queries in SparkUI and via explain, but both 3.0.1 and 3.1.1 produced identical plans.
Bug: Transform with udf gives incorrect result
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 14/Apr/21 21:46
Affects Version/s: 3.1.1

i think this is a correctness bug in spark 3.1.1

the behavior is correct in spark 3.0.1

in spark 3.0.1:
{code:java}
scala> import spark.implicits._

scala> import org.apache.spark.sql.functions._

scala> val x = Seq(Seq("aa", "bb", "cc")).toDF
x: org.apache.spark.sql.DataFrame = [value: array<string>]

scala> x.select(transform(col("value"), col => udf((_: String).drop(1)).apply(col))).show
+---------------------------------------------------+
|transform(value, lambdafunction(UDF(lambda 'x), x))|
+---------------------------------------------------+
|                                          [a, b, c]|
+---------------------------------------------------+
{code}
in spark 3.1.1:
{code:java}
scala> import spark.implicits._

scala> import org.apache.spark.sql.functions._

scala> val x = Seq(Seq("aa", "bb", "cc")).toDF
x: org.apache.spark.sql.DataFrame = [value: array<string>]

scala> x.select(transform(col("value"), col => udf((_: String).drop(1)).apply(col))).show
+---------------------------------------------------+
|transform(value, lambdafunction(UDF(lambda 'x), x))|
+---------------------------------------------------+
|                                          [c, c, c]|
+---------------------------------------------------+
{code}
Bug: inferSchema for type date 
Status: In Progress | Priority: Minor | Resolution: 
Created: 04/Apr/21 11:30
Affects Version/s: 3.1.1

Reading a csv file with `option({color:#6a8759}"inferSchema"{color}{color:#cc7832},{color}{color:#6a8759}"true"{color})` doesnt work with `date` type.  E.g. [https://github.com/apache/spark/blob/master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/csv/CSVInferSchema.scala#L101:L119] - can process only `Timestamp` not a `Date`

 

Datasets often contain `Date` type therefore reading a file to Spark should be able to infer `Date` type to a column.

For now, only work-arounds (e.g. [https://stackoverflow.com/a/46595057/1408096] , or [https://stackoverflow.com/questions/66935214/spark-reading-csv-with-specified-date-format] ) are possible/
Bug:  Coalesce shuffle partition should handle empty input RDD
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 27/Apr/21 07:11
Affects Version/s: 3.2.0

If input RDD partition is empty then the map output statistics will be null. And if all shuffle stage's input RDD partition is empty, we will skip it and lose the chance to coalesce partition.

 

We can simply create a empty partition for these custom shuffle reader to reduce the partition number.
Bug: DynamicFilter pushdown not working
Status: Open | Priority: Minor | Resolution: 
Created: 27/Apr/21 17:04
Affects Version/s: 3.1.1

 

The pushed filters is always empty. `PushedFilters: []`

I was expecting the filters to be pushed down on the probe side of the join.

Not sure how to properly configure this to work. For example how to set fallbackFilterRatio ?


spark = ( SparkSession.builder
 .master('local')
 .config("spark.sql.optimizer.dynamicPartitionPruning.fallbackFilterRatio", 100)
 .getOrCreate()
 )
 
df = spark.read.parquet('abfss://warehouse@<domain>/iceberg/opensource/<table>/data/timeperiod=2021-04-25/00000-0-929b48ef-7ec3-47bd-b0a1-e9172c2dca6a-00001.parquet')
df.createOrReplaceTempView('TMP')
 
spark.sql('''
explain cost
select 
   timeperiod, rrname
from
   TMP
where
   timeperiod in (
     select
       TO_DATE(d, 'MM-dd-yyyy') AS timeperiod
     from
     values
       ('01-01-2021'),
       ('01-01-2021'),
       ('01-01-2021') tbl(d)
 )
group by
   timeperiod, rrname
''').show(truncate=False)

|== Optimized Logical Plan ==
Aggregate [timeperiod#597, rrname#594], [timeperiod#597, rrname#594], Statistics(sizeInBytes=69.0 MiB)
+- Join LeftSemi, (timeperiod#597 = timeperiod#669), Statistics(sizeInBytes=69.0 MiB)
 :- Project [rrname#594, timeperiod#597], Statistics(sizeInBytes=69.0 MiB)
 : +- Relation[count#591,time_first#592L,time_last#593L,rrname#594,rrtype#595,rdata#596,timeperiod#597] parquet, Statistics(sizeInBytes=198.5 MiB)
 +- LocalRelation [timeperiod#669], Statistics(sizeInBytes=36.0 B)

== Physical Plan ==
*(2) HashAggregate(keys=[timeperiod#597, rrname#594], functions=[], output=[timeperiod#597, rrname#594])
+- Exchange hashpartitioning(timeperiod#597, rrname#594, 200), ENSURE_REQUIREMENTS, [id=#839]
 +- *(1) HashAggregate(keys=[timeperiod#597, rrname#594], functions=[], output=[timeperiod#597, rrname#594])
 +- *(1) BroadcastHashJoin [timeperiod#597], [timeperiod#669], LeftSemi, BuildRight, false
 :- *(1) ColumnarToRow
 : +- FileScan parquet [rrname#594,timeperiod#597] Batched: true, DataFilters: [], Format: Parquet, Location: InMemoryFileIndex[abfss://warehouse@<domain>/iceberg/opensource/..., PartitionFilters: [], PushedFilters: [], ReadSchema: struct<rrname:string,timeperiod:date>
 +- BroadcastExchange HashedRelationBroadcastMode(List(input[0, date, true]),false), [id=#822]
 +- LocalTableScan [timeperiod#669]
 
```