av9ash/GitBugs huggingface dataset:
Bug: TransformWithState metrics incorrectly counts the time for state removal
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 13/Nov/24 19:33
Affects Version/s: 4.0.0

In TWS, we use the streaming query progress metric `allRemovalsTimeMs` to actually count the amount of time it takes to process the user's timers. This is inconsistent with the other stateful operators, which use `allRemovalsTimeMs` to count the amount of time needed to remove state. The mis-labelling of the metric happens [here|https://github.com/apache/spark/blob/05508cf7cb9da3042fa4b17645102a6406278695/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/TransformWithStateExec.scala#L346].

Note that this likely resulted as a copy-paste bug, since `flatMapGroupsWithState` has the same bug [here|https://github.com/apache/spark/blob/05508cf7cb9da3042fa4b17645102a6406278695/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/FlatMapGroupsWithStateExec.scala#L132]. We should fix that too.

Additionally, TWS uses `commitTimeMs` to capture the amount of time taken to perform state removal due to TTL. This is incorrect; this should actually be part of `allRemovalsTimeMs`.
Bug: Row.jsonValue is not able to serialize LocalDateTime
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 20/Nov/24 10:51
Affects Version/s: 3.5.3

Trying to serialize to JSON a Row containing a column of type _TimestampNTZType_ ({_}LocalDateTime{_}) with methods:
{code:java}
def json: String = compact(jsonValue)
def prettyJson: String = pretty(render(jsonValue)) 
{code}
Will fail with:
{code:java}
[FAILED_ROW_TO_JSON] Failed to convert the row value '2018-05-14T12:13' of the class class java.time.LocalDateTime to the target SQL type "TIMESTAMPNTZTYPE" in the JSON format. SQLSTATE: 2203G
org.apache.spark.SparkIllegalArgumentException: [FAILED_ROW_TO_JSON] Failed to convert the row value '2018-05-14T12:13' of the class class java.time.LocalDateTime to the target SQL type "TIMESTAMPNTZTYPE" in the JSON format. SQLSTATE: 2203G
	at org.apache.spark.sql.Row.toJson$1(Row.scala:663)
	at org.apache.spark.sql.Row.toJson$1(Row.scala:651)
	at org.apache.spark.sql.Row.jsonValue(Row.scala:665)
	at org.apache.spark.sql.Row.jsonValue$(Row.scala:598)
	at org.apache.spark.sql.catalyst.expressions.GenericRow.jsonValue(rows.scala:28)
	at org.apache.spark.sql.RowJsonSuite.$anonfun$testJson$1(RowJsonSuite.scala:41)
{code}
This happens because the toJson method inside of the class is missing a serializer for the LocalDateTime type
{code:java}
    def toJson(value: Any, dataType: DataType): JValue = (value, dataType) match {
      case (null, _) => JNull
      case (b: Boolean, _) => JBool(b)
      case (b: Byte, _) => JLong(b)
      case (s: Short, _) => JLong(s)
      case (i: Int, _) => JLong(i)
      case (l: Long, _) => JLong(l)
...
{code}
Bug: Introduce a shared plan representation
Status: Open | Priority: Major | Resolution: 
Created: 20/Nov/24 14:08
Affects Version/s: 4.0.0

Introduce a stable structured plan representation that can be used by both Connect and Classic. This can be used by explain, queryExecution, or a lightweight version of QueryExecutionListener.

This plan representation should support the following things:
 * (Parsed/Logical/Pysical)Plans
 ** Schema
 ** Name
 ** Children
 * Expressions
 ** Name
 ** Children
 * Subqueries
 * Reuse of subqueries/plans.
 * Metrics
 * (optionally) Statistics

The plan representation must be serializable to and from proto.

For connect we may want to send it at the end of query execution. We do need to think about limits here.
Bug: Row.jsonValue is not able to serialize LocalDateTime
Status: Resolved | Priority: Major | Resolution: Duplicate
Created: 20/Nov/24 10:51
Affects Version/s: 3.5.3

Trying to serialize to JSON a Row containing a column of type _TimestampNTZType_ ({_}LocalDateTime{_}) with methods:
{code:java}
def json: String = compact(jsonValue)
def prettyJson: String = pretty(render(jsonValue)) 
{code}
Will fail with:
{code:java}
[FAILED_ROW_TO_JSON] Failed to convert the row value '2018-05-14T12:13' of the class class java.time.LocalDateTime to the target SQL type "TIMESTAMPNTZTYPE" in the JSON format. SQLSTATE: 2203G
org.apache.spark.SparkIllegalArgumentException: [FAILED_ROW_TO_JSON] Failed to convert the row value '2018-05-14T12:13' of the class class java.time.LocalDateTime to the target SQL type "TIMESTAMPNTZTYPE" in the JSON format. SQLSTATE: 2203G
	at org.apache.spark.sql.Row.toJson$1(Row.scala:663)
	at org.apache.spark.sql.Row.toJson$1(Row.scala:651)
	at org.apache.spark.sql.Row.jsonValue(Row.scala:665)
	at org.apache.spark.sql.Row.jsonValue$(Row.scala:598)
	at org.apache.spark.sql.catalyst.expressions.GenericRow.jsonValue(rows.scala:28)
	at org.apache.spark.sql.RowJsonSuite.$anonfun$testJson$1(RowJsonSuite.scala:41)
{code}
This happens because the toJson method inside of the class is missing a serializer for the LocalDateTime type
{code:java}
    def toJson(value: Any, dataType: DataType): JValue = (value, dataType) match {
      case (null, _) => JNull
      case (b: Boolean, _) => JBool(b)
      case (b: Byte, _) => JLong(b)
      case (s: Short, _) => JLong(s)
      case (i: Int, _) => JLong(i)
      case (l: Long, _) => JLong(l)
...
{code}
Bug: Failed to execute ALTER TABLE ADD COLUMNS because of incompatible type
Status: Open | Priority: Major | Resolution: 
Created: 19/Nov/24 06:08
Affects Version/s: 3.5.3

How to reproduce:

{noformat}
spark-sql (default)> CREATE TABLE t(id int) using parquet; 
Time taken: 0.812 seconds
spark-sql (default)> ALTER TABLE t ADD COLUMN PROCESSSED varchar(1) DEFAULT 'N';
[INVALID_DEFAULT_VALUE.DATA_TYPE] Failed to execute ALTER TABLE ADD COLUMNS command because the destination table column `PROCESSSED` has a DEFAULT value 'N', which requires "VARCHAR(1)" type, but the statement provided a value of incompatible "STRING" type.
{noformat}
Bug: TransformWithState timers should compare expiration with less than or equal to
Status: Open | Priority: Major | Resolution: 
Created: 19/Nov/24 05:32
Affects Version/s: 4.0.0

Today, TransformWithState will expire a timer expiring at time T when the expiration timestamp is _greater_ than it. However, Structured Streaming in general treats watermarks as the timestamp before which no more records will be received. Thus, when the event-time is at T, a window ending at T can be closed. To be consistent, TransformWithState should fire a timer at time T when the expiration timestamp is also T.

(That's a long way of saying that [this line|https://github.com/apache/spark/blob/5a57efdcee9e6569d8de4272bda258788cf349e3/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/TimerStateImpl.scala#L181] should be <=, not <.)
Bug: Add documentation for SQL pipe syntax
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 14/Nov/24 00:09
Affects Version/s: 4.0.0

We can create a file `spark/docs/sql-pipe-syntax.md`(1) which contains a description of how to use the syntax, how each clause works, and examples.

(1) https://github.com/apache/spark/tree/5cc60f46708844c812ee0f21bee4f4b4b70c6d92/docs
Bug: Cannot resolve parameterized identifier in a sub-query
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 14/Nov/24 19:33
Affects Version/s: 4.0.0

The example below portraits the issue:

{code:sq}
create table tt1(c1 int);
insert into tt1 values (1);
execute immediate 'with v1 as (select * from tt1 where 1 = (Select * from identifier(:tab))) select * from v1' using 'tt1' as tab;
[UNBOUND_SQL_PARAMETER] Found the unbound parameter: tab. Please, fix `args` and provide a mapping of the parameter to either a SQL literal or collection constructor functions such as `map()`, `array()`, `struct()`. SQLSTATE: 42P02; line 1 pos 66;
'Project [*]
+- 'SubqueryAlias v1
   +- 'SubqueryAlias v1
      +- 'Project [*]
         +- 'Filter (1 = scalar-subquery#10 [])
            :  +- 'Project [*]
            :     +- 'PlanWithUnresolvedIdentifier namedparameter(tab), org.apache.spark.sql.catalyst.parser.AstBuilder$$Lambda$3404/0x000000f802380000@45ec7633
            +- SubqueryAlias spark_catalog.default.tt1
               +- Relation spark_catalog.default.tt1[c1#4] parquet
{code}
Bug: Move function resolution code out of Analyzer.scala
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 15/Nov/24 09:47
Affects Version/s: 4.0.0

Some Analyzer code will be used in both fixed-point and single-pass Analyzers. Also, Analyzer.scala is 4K+ lines long, so it makes sense to gradually split it.

We can move code function resolution code out to a separate object, because it's reasonably self-contained.

Context: https://issues.apache.org/jira/browse/SPARK-49834
Bug: Correct MakeDTInterval and MakeYMInterval to catch Java exceptions
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 05/Nov/24 08:33
Affects Version/s: 4.0.0

While working on MakeInterval, it was noticed that MakeDTInterval and MakeYMInterval do not catch Java exceptions in nullSafeEval, but just call utility functions. This needs to be fixed, so that we conform to the error system.
Bug: Move alias resolution code out of Analyzer.scala
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 15/Nov/24 08:51
Affects Version/s: 4.0.0

Some Analyzer code will be used in both fixed-point and single-pass Analyzers. Also, Analyzer.scala is 4K+ lines long, so it makes sense to gradually split it.

We can move code alias resolution code out to a separate object, because it's reasonably self-contained.

Context: https://issues.apache.org/jira/browse/SPARK-49834
Bug: SparkThriftServer createServer parameter passing error  when kerberos is true
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 14/Nov/24 09:53
Affects Version/s: 3.4.4

SparkThriftServer createServer parameter passing error  when kerberos is true
{code:java}
saslServer = ShimLoader.getHadoopThriftAuthBridge().createServer(principal, keytab); {code}
{code:java}
public Server createServer(String keytabFile, String principalConf) throws TTransportException {
  return new Server(keytabFile, principalConf);
} {code}
Bug: Use mirror host instead of `archive.apache.org`
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 13/Nov/24 18:30
Affects Version/s: 4.0.0

 

*AFTER*
{code:java}
$ build/mvn clean
exec: curl --retry 3 --silent --show-error -L https://www.apache.org/dyn/closer.lua/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz?action=download
exec: curl --retry 3 --silent --show-error -L https://www.apache.org/dyn/closer.lua/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz.sha512?action=download {code}
Bug: JDBC code stack does not capture collated types in StringType
Status: Resolved | Priority: Major | Resolution: Won't Do
Created: 04/Nov/24 15:32
Affects Version/s: 4.0.0

There are a lot places in the `sql/core/src/main/scala/org/apache/spark/sql/jdbc` where we have `case StirngType: `. This pattern matching will not capture the collated `StringType`, so these pieces of code should be changed `case _: StringType`.
Bug: Avoid using a classproperty in threadpool for Python Connect client
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 13/Nov/24 04:25
Affects Version/s: 4.0.0

> Deprecated since version 3.11, removed in version 3.13: Class methods can no longer wrap other descriptors such as property().

It causes failure at https://github.com/apache/spark/actions/runs/11804766326/job/32885813371
Bug: JavaOptions Injection Issue
Status: Open | Priority: Major | Resolution: 
Created: 06/Nov/24 06:58
Affects Version/s: 3.5.1

First, we need to obtain regular user privileges and construct a JAR file according to custom rules.When yarn submits a task, I put a command-injection string on a parameter named "spark.executor.extraJavaOptions", it's about "\'touch\$IFS/tmp/czjtest\'"

There are two ways to submit parameters：
 # Using command lines：spark-submit --class JavaWordCount --master yarn --deploy-mode client --conf spark.executor.extraJavaOptions="\`touch\$IFS/tmp/czjtest\`" test.jar
 # Using java API：sparkLauncher.setConf("spark.executor.extraJavaOptions", "`touch$IFS/tmp/czjtest`");

*We may find command-injection logs in hadoop.*

I found the vulnerability exposed in file `ExexcutorRunnable.scale`：

 
{code:java}
//Set extra Java options for the executor, if defined
sparkConf.get(EXECUTOR_JAVA_OPTIONS).foreach{  opt => val
subsOpt = Utils.subtitudeAppNExecIds(opts, appId, executorId) javaOpts ++= Utils.splitCommandString(subsOpt).map(YarnSparkHadopUtil.escapeForShell)
}
{code}
CVSS score: 8.8（AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H）

In the directory {{{}Bigdata/common/runtime/security{}}}, there are security configurations, and the {{krb5.conf}} file is readable by the {{{}ldapuser{}}}, with the key location accessible.

In {{{}Bigdata/FusionInsight_Current/1_5_DataNode/install/hadoop{}}}, the {{ldapuser}} has read and execute permissions, allowing the execution of commands such as {{{}hadoop{}}}, {{{}hdfs{}}}, and {{{}yarn{}}}.

In {{{}Bigdata/FusionInsight_Current/1_6_NodeManager/etc{}}}, the {{ldapuser}} has read permissions, and there are business configurations present.

In {{{}Bigdata/FusionInsight_Current/1_6_NodeManager/install/hadoop{}}}, the {{ldapuser}} has read and execute permissions, allowing the execution of commands such as {{{}hadoop{}}}, {{{}hdfs{}}}, and {{{}yarn{}}}.

In {{{}Bigdata/FusionInsight_Current/1_8_RegionServer{}}}, the {{ldapuser}} has read permissions on a few files, and there are business configurations present.
Bug: pyspark local session `spark.jars` configuration does not work 
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 11/Nov/24 08:09
Affects Version/s: 4.0.0

pyspark local session `spark.jars` configuration does not work 

 

Reproducing code:

 

if I do the following in a python shell:
{code:java}
from pyspark.sql import SparkSession
spark=SparkSession.builder.config("spark.jars", <my_jar_file_path>).master("local[*]").getOrCreate() {code}
then do:
{code:java}
spark.sparkContext._jvm.aaa.bbb.ccc  # aaa.bbb.ccc is the class in the jar file
{code}
it can't access the Java class correctly.

 

but if I do:
{code:java}
bin/pyspark --jars=<my_jar_file_path>{code}
then in the pyspark shell, the issue vanished.

 

this issue only happens in spark master, and it causes our CI failure:

[https://github.com/mlflow-automation/mlflow/actions/runs/11765501211/job/32775243482#step:12:291]
Bug: Create easy API in Python to construct Variants
Status: Open | Priority: Major | Resolution: 
Created: 11/Nov/24 20:53
Affects Version/s: 4.0.0

Currently, `VariantVal`'s cannot be created in Python. The `VariantVal` type can only be used to read Variants from existing DataFrames. In order to create Variants, one can only hard-code byte values of Variants (like `VariantVal(bytes([12, i]), bytes([1, 0, 0]))`) which is not useful at all. There should be some easy way for Python developers to create variants from JSON strings/potentially other ways, for example a `parse_json` API implementation in Python.
Bug: Add JSON path expression using the COLON operator
Status: Open | Priority: Major | Resolution: 
Created: 09/Nov/24 05:44
Affects Version/s: 4.0.0

Support JSON path expression by adding COLON sign operator [1] and JSON path expression [2] 

 [1] [https://docs.databricks.com/en/sql/language-manual/functions/colonsign.html] 

 [2] [https://docs.databricks.com/en/sql/language-manual/sql-ref-json-path-expression.html]

 

Syntax:
{code:java}
jsonExpr : jsonPath
{code}
jsonExpr can be either a VariantType or a valid JSON String.
 * when jsonExpr is a valid JSON String, use an inner function *semi_structured_extract_json_multi* that is to be added with a new RegexParser such that the jsonPath is more flexible than  [get_json_object|https://spark.apache.org/docs/4.0.0-preview1/api/sql/#get_json_object]).
 * when jsonExpr is a VariantType, apply the built-in function [variant_get|https://spark.apache.org/docs/4.0.0-preview1/api/sql/#variant_get]
Bug: Forbid change complex types during altering collation
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 07/Nov/24 13:33
Affects Version/s: 4.0.0

SPARK-48413 has brought ability to change collation on table.
But it seems to be too wide, because people couldn't change the whole StructType/MapType/ArrayType by altering before.
Right now there is found problem with altering collation when table has metadata. Altering will be failed because of a metadata mismatch between user's type (that doesn't have metadata) and existing schema.
So I suggest to shrink the feature to only leaf string types.
People will be able to change any collation still, but they need to use full column path:

{code:sql}
ALTER TABLE table ALTER COLUMN full.column.path TYPE STRING COLLATE UNICODE
{code}
Bug: Artifact isolation for Hive
Status: Open | Priority: Major | Resolution: 
Created: 06/Nov/24 14:18
Affects Version/s: 4.0.0

Problems:
* ADD JAR is now isolated because it's an SQL command.
* We now disable isolation, especially for Hive, so ADD JAR in Hive is not isolated.
* Meanwhile in non-Hive env ADD JAR is still isolated.

We need to decide how to handle and close this discrepancy.
Bug: Add option for lightweight plan validation which is enabled by default
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 07/Nov/24 01:11
Affects Version/s: 4.0.0

Currently, plan validation is disabled by default (only runs enabled by default in tests). We can extend this to a "lightweight" version that is run by default for all queries, but is quick to evaluate and provides useful debugging information (e.g. which rule caused the plan to become invalid).
Bug: [Core]If a stage contains ExpandExec,  the CoalesceShufflePartitions rule will not be adjusted during the AQE phase
Status: Open | Priority: Major | Resolution: 
Created: 07/Nov/24 03:38
Affects Version/s: 4.0.0

【sql】
{code:java}
// code placeholder
SELECT
       /*+ SHUFFLE_MERGE(b) */
       s_date,
       sum(s_quantity * i_price) AS total_sales
   FROM
       sales a
       JOIN items b ON s_item_id = i_item_id
   WHERE
       i_price < 10
   GROUP BY
       s_date with rollup;
 {code}
Set spark.sql.shuffle.partitions=1000

After aqe:

!截屏2024-11-07 13.52.45.png|width=444,height=431!

The parallel reads in the ExpandExecut phase have been adjusted to 71, reducing parallelism. The ExpandExecut phase can lead to data expansion, and a decrease in parallelism can result in longer task execution times.

If AGE is turned off as a whole, AQE optimization cannot be enjoyed in other stages. If it is found that ExpandExec is included in the current stage, partition merging will not be performed for this issue.
Bug: Move the RewriteCollationJoin rule to FinishAnalysis
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 05/Nov/24 16:51
Affects Version/s: 4.0.0

Such conversions should be NOT considered as an optimization, but it is needed for correctness. So, we cannot run the substitutions as an optimization rule, it should be applied before any.

We could modify RewriteCollationJoin  and take into account DPP, but we might miss something else. Fxing ALL results of optimizations in RewriteCollationJoin  introduces some dependencies.
Bug: Reduce memory usage on driver for wide schemas by reducing the lifetime of AttributeReference objects created during logical planning
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 05/Nov/24 17:11
Affects Version/s: 4.0.0

The allAttributes method in QueryPlan ([code|https://github.com/apache/spark/blob/57f6824e78e2e615778827ddebce9d7fcaae1698/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/basicLogicalOperators.scala#L2101]) unions the output of all of its children. Although this is okay in an optimized plan, in a pre-optimized analyzed plan, these attributes add up multiplicatively with the size of the plan. Because the `output` is usually defined as a `def`, each node’s allAttributes also ends up with a distinct copy of each attribute, potentially causing significant memory pressure on the driver (especially under concurrency and with wide tables).

Here is a simple example with TPC-DS Q42. SQL:

 
{code:sql}
select dt.d_year, item.i_category_id, item.i_category, sum(ss_ext_sales_price)
 from 	date_dim dt, store_sales, item
 where dt.d_date_sk = store_sales.ss_sold_date_sk
 	and store_sales.ss_item_sk = item.i_item_sk
 	and item.i_manager_id = 1
 	and dt.d_moy=11
 	and dt.d_year=2000
 group by 	dt.d_year
 		,item.i_category_id
 		,item.i_category
 order by       sum(ss_ext_sales_price) desc,dt.d_year
 		,item.i_category_id
 		,item.i_category
 limit 100
{code}
If we print out the size of each operator’s output and the size of its `allAttributes`:
{code:java}
GlobalLimit: allAttrs: 4, output: 4
LocalLimit: allAttrs: 4, output: 4
Sort: allAttrs: 4, output: 4
Aggregate: allAttrs: 73, output: 4
Filter: allAttrs: 73, output: 73
Join: allAttrs: 73, output: 73
Join: allAttrs: 51, output: 51
SubqueryAlias: allAttrs: 28, output: 28
SubqueryAlias: allAttrs: 28, output: 28
LogicalRelation: allAttrs: 0, output: 28
SubqueryAlias: allAttrs: 23, output: 23
LogicalRelation: allAttrs: 0, output: 23
SubqueryAlias: allAttrs: 22, output: 22
LogicalRelation: allAttrs: 0, output: 22{code}
 

Note how the joins and aggregate have 73 attributes each, by adding the width of each relation. For queries with wide schemas, this issue is much worse. Optimized plans after column pruning look far better:
{code:java}
Aggregate: allAttrs: 0, output: 1
Project: allAttrs: 2, output: 0
Join: allAttrs: 2, output: 2
Project: allAttrs: 3, output: 1
Join: allAttrs: 3, output: 3
Project: allAttrs: 4, output: 2
Join: allAttrs: 4, output: 4
Project: allAttrs: 5, output: 3
Join: allAttrs: 5, output: 5
Project: allAttrs: 23, output: 4
Filter: allAttrs: 23, output: 23
LogicalRelation: allAttrs: 0, output: 23
Project: allAttrs: 29, output: 1
Filter: allAttrs: 29, output: 29
LogicalRelation: allAttrs: 0, output: 29
Project: allAttrs: 13, output: 1
Filter: allAttrs: 13, output: 13
LogicalRelation: allAttrs: 0, output: 13
Project: allAttrs: 22, output: 1
Filter: allAttrs: 22, output: 22
LogicalRelation: allAttrs: 0, output: 22
Project: allAttrs: 18, output: 1
Filter: allAttrs: 18, output: 18
LogicalRelation: allAttrs: 0, output: 18 {code}
Bug: Add 'protobuf==5.28.3' in dev/requirements.txt
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 05/Nov/24 17:52
Affects Version/s: 4.0.0

After https://github.com/apache/spark/pull/48644, it requires protobuf 5.28.3 to be installed for dev. We should note it in dev/requirements.txt
Bug: Refactor ApplyCharTypePadding so that helper methods can be used in single-pass resolver
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 04/Nov/24 17:51
Affects Version/s: 4.0.0

Refactor Refactor ApplyCharTypePadding so that helper methods can be used in single-pass resolver. This means refactoring these helpers to a separate object and moving it to catalyst package.
Bug: Factor out `HiveTableRelation` read path resolution
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 01/Nov/24 14:07
Affects Version/s: 4.0.0

Factor out `HiveTableRelation` resolution into a separate `RelationConversions` method to reuse it in the single-pass Analyzer.

Context: https://issues.apache.org/jira/browse/SPARK-49834