av9ash/GitBugs huggingface dataset:
Bug: next_day function should throw runtime exception when receiving invalid input under ANSI mode
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 15/Dec/20 13:35
Affects Version/s: 3.0.0

Hello all,

According to [ANSI compliance|https://spark.apache.org/docs/3.0.0/sql-ref-ansi-compliance.html#ansi-compliance], the [next_day function|https://github.com/apache/spark/blob/master/sql/core/src/main/scala/org/apache/spark/sql/functions.scala#L3095] should throw an runtime exception when receiving invalid value for dayOfWeek, exemple receiving "xx" instead of "SUNDAY".

 

A similar improvement has been done on the element_at function: https://issues.apache.org/jira/browse/SPARK-33386

 

If you agree with this proposition, i can submit a pull request with necessary change.

 

Kind regardes,
Bug: GetMapValueUtil code generation error
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 01/Dec/20 09:23
Affects Version/s: 3.1.0

```

In -SPARK-33460---

there is a bug as follow.

GetMapValueUtil generated code error when ANSI mode is on

 

s"""throw new NoSuchElementException("Key " + $eval2 + " does not exist.");"""

should be 

s"""throw new java.util.NoSuchElementException("Key " + $eval2 + " does not exist.");"""

 

But Why are

checkExceptionInExpression[Exception](expr, errMsg)

and sql/testOnly org.apache.spark.sql.SQLQueryTestSuite – -z ansi/map.sql

can't detect this Bug

 

it's because 

1. checkExceptionInExpression is some what error, too. it should wrap with 

withSQLConf(SQLConf.CODEGEN_FACTORY_MODE.key like CheckEvalulation, AND WE SHOULD FIX this later.

2. SQLQueryTestSuite ansi/map.sql failed to detect because of the ConstantFolding rules, it is calling eval instead of code gen  in this case

 

```
Bug: Make Spark DAGScheduler datasource cache aware when scheduling tasks in a multi-replication HDFS
Status: Open | Priority: Major | Resolution: 
Created: 24/Dec/20 03:10
Affects Version/s: 3.0.0

*Goals:*
•	Make Spark 3.0 Scheduler DataSource-Cache-Aware in multi-replication HDFS cluster
•	Performance gain in E2E workload when enabling this feature

*Problem Statement:*
Spark’s DAGScheduler currently schedule tasks according to RDD’s preferLocations, which repects HDFS BlockLocation. In a multi-replication cluster, HDFS BlockLocation can be returned as an Array[BlockLocation], Spark chooses one of the BlockLocation to run tasks on. +However,tasks can run faster if scheduled to the nodes with datasource cache that they need. Currently there’re no datasource cache locality provision mechanism in Spark if nodes in the cluster have cache data+.
This project aims to add a cache-locality-aware mechanism. Spark DAGScheduler can schedule tasks to the nodes with datasource cache according to cache locality in a multi-replication HDFS.

*Basic idea:*
The basic idea is to open a datasource cache locality provider interface in Spark and with default implementation is to respect HDFS BlockLocation. Worker nodes datasource cache meta(like offset, length) needs to be stored in an externalDB like Redis. Spark driver can look up these cache meta and customize task schedule locality algorithm to choose the most efficient node.

*CBL(Cost Based Locality)*
CBL(cost based locality), takes cache size、disk IO、network IO...... into account when scheduling tasks.
Say there’re 3 nodes A、B、C in a 2-replication HDFS cluster. When Spark scheduling task1, nodeB have all the data replication on disk that task1 needs, at the same time, nodeA has 20% datasource cache and 50% data replication on disk.

Then we calculate the cost for schedule task1 on nodeA、nodeB and nodeC.

CostA = CalculateCost(20% read from cache) + CalculateCost(50% read from disk) + CalculateCost(30% read from remote)

CostB = CalculateCost(100% read from disk)

CostC = CalculateCost(100% read from remote)

Return the node with minimal cost.


*Modifications:*
A config is needed to decide which cache locality provider to use, can be as follows

{code:java}
SQLConf.PARTITIONED_FILE_PREFERREDLOC_IMPL
{code}

For Spark3.0 need to modify FilePartition.scala$preferredLocations() can be as follows

{code:java}
override def preferredLocations(): Array[String] = {
Utils.classForName(SparkEnv.get.conf.get(SQLConf.PARTITIONED_FILE_PREFERREDLOC_IMPL))
    	. getConstructor()
      	. newInstance()
	. getPreferredLocs()
}

{code}
Bug: Allow json expression to be pushable column
Status: In Progress | Priority: Major | Resolution: 
Created: 26/Dec/20 04:00
Affects Version/s: 3.0.1

Currently PushableColumnBase provides no support for json / jsonb expression.

Example of json expression:
{code}
get_json_object(phone, '$.code') = '1200'
{code}
If non-string literal is part of the expression, the presence of cast() would complicate the situation.

Implication is that implementation of SupportsPushDownFilters doesn't have a chance to perform pushdown even if third party DB engine supports json expression pushdown.

This issue is for discussion and implementation of Spark core changes which would allow json expression to be recognized as pushable column.
Bug: Support latest version of initialize() in HiveGenericUDTF
Status: Resolved | Priority: Major | Resolution: Duplicate
Created: 08/Dec/20 06:33
Affects Version/s: 2.2.0

For HiveGenericUDTF , there are two initialization methods:
{code:java}
  public StructObjectInspector initialize(StructObjectInspector argOIs)
      throws UDFArgumentException {
    List<? extends StructField> inputFields = argOIs.getAllStructFieldRefs();
    ObjectInspector[] udtfInputOIs = new ObjectInspector[inputFields.size()];
    for (int i = 0; i < inputFields.size(); i++) {
      udtfInputOIs[i] = inputFields.get(i).getFieldObjectInspector();
    }
    return initialize(udtfInputOIs);
  }
  @Deprecated
  public StructObjectInspector initialize(ObjectInspector[] argOIs)
      throws UDFArgumentException {
    throw new IllegalStateException("Should not be called directly");
  }

{code}
As https://issues.apache.org/jira/browse/HIVE-5737 mentioned, hive provided StructObjectInspector for UDTFs rather than ObjectInspect[], but Spark SQL  still only support deprecated function.

An exception will be reported before fix:

Error in query: No handler for UDF/UDAF/UDTF 'FeatureParseUDTF1': java.lang.IllegalStateException: Should not be called directly
Please make sure your function overrides public StructObjectInspector initialize(ObjectInspector[] args).; line 1 pos 7
Bug: Recognize `spark_catalog` in `saveAsTable()` and `insertInto()`
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 24/Dec/20 17:41
Affects Version/s: 3.2.0

The v1 INSERT INTO command recognizes `spark_catalog` as the default session catalog:
{code:sql}
spark-sql> create table spark_catalog.ns.tbl (c int);
spark-sql> insert into spark_catalog.ns.tbl select 0;
spark-sql> select * from spark_catalog.ns.tbl;
0
{code}
but the `saveAsTable()` and `insertInto()` methods don't allow to write a table with explicitly specified catalog spark_catalog:
{code:scala}
scala> sql("CREATE NAMESPACE spark_catalog.ns")
scala> Seq(0).toDF().write.saveAsTable("spark_catalog.ns.tbl")
org.apache.spark.sql.AnalysisException: Couldn't find a catalog to handle the identifier spark_catalog.ns.tbl.
  at org.apache.spark.sql.DataFrameWriter.saveAsTable(DataFrameWriter.scala:629)
  ... 47 elided
scala> Seq(0).toDF().write.insertInto("spark_catalog.ns.tbl")
org.apache.spark.sql.AnalysisException: Couldn't find a catalog to handle the identifier spark_catalog.ns.tbl.
  at org.apache.spark.sql.DataFrameWriter.insertInto(DataFrameWriter.scala:498)
  ... 47 elided
{code}
Bug: Support V2 ALTER TABLE .. RENAME PARTITION
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 20/Dec/20 09:21
Affects Version/s: 3.2.0

Need to implement v2 execution node for ALTER TABLE .. RENAME PARTITION similar to v1 implementation: https://github.com/apache/spark/blob/40c37d69fd003ed6079ee8c139dba5c15915c568/sql/core/src/main/scala/org/apache/spark/sql/execution/command/ddl.scala#L513
Bug: Replace None of elseValue inside CaseWhen if all branches are FalseLiteral
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 19/Dec/20 08:38
Affects Version/s: 3.2.0

{code:scala}
spark.sql("create table t1 using parquet as select id from range(10)")
spark.sql("select id from t1 where (CASE WHEN id = 1 THEN 'a' WHEN id = 3 THEN 'b' end) = 'c' ").explain()
{code}

Before:
{noformat}
== Physical Plan ==
*(1) Filter CASE WHEN (id#1L = 1) THEN false WHEN (id#1L = 3) THEN false END
+- *(1) ColumnarToRow
   +- FileScan parquet default.t1[id#1L] Batched: true, DataFilters: [CASE WHEN (id#1L = 1) THEN false WHEN (id#1L = 3) THEN false END], Format: Parquet, Location: InMemoryFileIndex[file:/Users/yumwang/opensource/spark/spark-warehouse/org.apache.spark.sql.DataF..., PartitionFilters: [], PushedFilters: [], ReadSchema: struct<id:bigint>

{noformat}

After:

{noformat}
== Physical Plan ==
LocalTableScan <empty>, [id#1L]
{noformat}
Bug: Simplify CaseWhenclauses with (true and false) and (false and true)
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 23/Dec/20 01:29
Affects Version/s: 3.2.0

Simplify CaseWhenclauses with (true and false) and (false and true):
||Expression||After simplify||
|case when cond then true else false end|cond|
|case when cond then false else true end|!cond|
Bug: v2 INSERT INTO .. PARTITION drops the partition location
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 28/Dec/20 08:23
Affects Version/s: 3.2.0

See the test "with location" in v2.AlterTableRenamePartitionSuite:
{code:scala}
val loc = "location1"
sql(s"ALTER TABLE $t ADD PARTITION (id = 2) LOCATION '$loc'")
checkLocation(t, Map("id" -> "2"), loc)

sql(s"INSERT INTO $t PARTITION (id = 2) SELECT 'def'")
checkLocation(t, Map("id" -> "2"), loc)
{code}
The second check must not fail.
Bug: Push the UnaryExpression into (if / case) branches
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 19/Dec/20 11:56
Affects Version/s: 3.2.0

Push the cast into (if / case) branches. The use case is:

{code:sql}
create table t1 using parquet as select id from range(10);
explain select id from t1 where (CASE WHEN id = 1 THEN '1' WHEN id = 3 THEN '2' end) > 3;
{code}

Before this pr:
{noformat}
== Physical Plan ==
*(1) Filter (cast(CASE WHEN (id#1L = 1) THEN 1 WHEN (id#1L = 3) THEN 2 END as int) > 3)
+- *(1) ColumnarToRow
   +- FileScan parquet default.t1[id#1L] Batched: true, DataFilters: [(cast(CASE WHEN (id#1L = 1) THEN 1 WHEN (id#1L = 3) THEN 2 END as int) > 3)], Format: Parquet, Location: InMemoryFileIndex[file:/Users/yumwang/opensource/spark/spark-warehouse/org.apache.spark.sql.DataF..., PartitionFilters: [], PushedFilters: [], ReadSchema: struct<id:bigint>
{noformat}


After this pr:
{noformat}
== Physical Plan ==
LocalTableScan <empty>, [id#1L]
{noformat}
Bug:  Improve SimplifyConditionals
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 19/Dec/20 04:30
Affects Version/s: 3.2.0

Simplify If(cond, TrueLiteral, FalseLiteral) to cond.
Simplify If(cond, FalseLiteral, TrueLiteral) to Not(cond).
Bug: v1 SHOW TABLES fails with assert on spark_catalog
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 24/Dec/20 09:01
Affects Version/s: 3.2.0

The v1 SHOW TABLES, SHOW TABLE EXTENDED and SHOW VIEWS fail with internal assert when a database is not specified:
{code:sql}
spark-sql> show tables in spark_catalog;
20/12/24 11:19:46 ERROR SparkSQLDriver: Failed in [show tables in spark_catalog]
java.lang.AssertionError: assertion failed
	at scala.Predef$.assert(Predef.scala:208)
	at org.apache.spark.sql.catalyst.analysis.ResolveSessionCatalog$$anonfun$apply$1.applyOrElse(ResolveSessionCatalog.scala:366)
	at org.apache.spark.sql.catalyst.analysis.ResolveSessionCatalog$$anonfun$apply$1.applyOrElse(ResolveSessionCatalog.scala:49)
	at org.apache.spark.sql.catalyst.plans.logical.AnalysisHelper.$anonfun$resolveOperatorsUp$3(AnalysisHelper.scala:90)
	at org.apache.spark.sql.catalyst.trees.CurrentOrigin$.withOrigin(TreeNode.scala:73)
{code}
Bug: We cannot pass schema to a createDataFrame function in scala, however we can do this in python.
Status: Open | Priority: Major | Resolution: 
Created: 27/Dec/20 10:48
Affects Version/s: 3.0.1

~spark.createDataFrame(data, schema)~

~I am able to pass schema as a parameter to a function createDataFrame in python but cannot pass this in scala for static data.~
Bug: Unload State Store asap once it becomes inactive
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 17/Dec/20 18:09
Affects Version/s: 3.2.0

SS maintains state stores in executors across batches. Due to the nature of Spark scheduling, a state store might be allocated on another executor in next batch. The state store in previous batch becomes inactive.

Now we run a maintenance task periodically to unload inactive state stores. So there will be some delays between a state store becomes inactive and it is unloaded.

Per the discussion on https://github.com/apache/spark/pull/30770 with [~kabhwan], I think the preference is to unload inactive state store asap.

However, we can force Spark to always allocate a state store to same executor, by using task locality configuration. This can reduce the possibility to have inactive state store.

Normally, I think with locality configuration, we might not able to see inactive state store generally. There is still chance an executor can be failed and reallocated, but in this case, inactive state store is also lost too. So it is not an issue.

So unloading inactive store asap is only useful when we don't use task locality to force state store locality across batches.

The required change to make driver-executor bi-directional for state store management looks non-trivial. If we already can reduce possibility of inactive store, is it still worth making non-trivial here?
Bug: UnresolvedView should retain SQL text position
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 27/Dec/20 03:15
Affects Version/s: 3.2.0

UnresolvedView should retain SQL text position. The following commands will be handled:

"DROP VIEW v"
"ALTER VIEW v SET TBLPROPERTIES ('k'='v')"
"ALTER VIEW v UNSET TBLPROPERTIES ('k')"
"ALTER VIEW v AS SELECT 1"
Bug: Throw PartitionsAlreadyExistException from HiveExternalCatalog.createPartitions()
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 10/Dec/20 17:46
Affects Version/s: 2.4.7

HiveExternalCatalog.createPartitions throws AlreadyExistsException wrapped by AnalysisException. The behavior deviates from V1/V2 in-memory catalogs that throw PartitionsAlreadyExistException.
Bug: Throw `PartitionAlreadyExistsException` if the target partition exists in Hive v1 Table Catalog
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 20/Dec/20 19:12
Affects Version/s: 3.2.0

Currently, ALTER TABLE .. RENAME PARTITION for Hive V1 Table Catalog throws AnalysisException when the target partition exists.
Bug: Add new rule to push down the foldable expressions through CaseWhen/If
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 16/Dec/20 01:20
Affects Version/s: 3.2.0

Simplify CaseWhen/If with EqualTo if all values are Literal and always false, this is a real case from production:
{code:sql}
create table t1 using parquet as select * from range(100);
create table t2 using parquet as select * from range(200);

create temp view v1 as                                                             
select 'a' as event_type, * from t1                                                
union all                                                                          
select CASE WHEN id = 1 THEN 'b' WHEN id = 3 THEN 'c' end as event_type, * from t2 

explain select * from v1 where event_type = 'a';
{code}

Before this PR:


{noformat}
== Physical Plan ==
Union
:- *(1) Project [a AS event_type#30533, id#30535L]
:  +- *(1) ColumnarToRow
:     +- FileScan parquet default.t1[id#30535L] Batched: true, DataFilters: [], Format: Parquet
+- *(2) Project [CASE WHEN (id#30536L = 1) THEN b WHEN (id#30536L = 3) THEN c END AS event_type#30534, id#30536L]
   +- *(2) Filter (CASE WHEN (id#30536L = 1) THEN b WHEN (id#30536L = 3) THEN c END = a)
      +- *(2) ColumnarToRow
         +- FileScan parquet default.t2[id#30536L] Batched: true, DataFilters: [(CASE WHEN (id#30536L = 1) THEN b WHEN (id#30536L = 3) THEN c END = a)], Format: Parquet
{noformat}

After this PR:


{noformat}
== Physical Plan ==
*(1) Project [a AS event_type#8, id#4L]
+- *(1) ColumnarToRow
   +- FileScan parquet default.t1[id#4L] Batched: true, DataFilters: [], Format: Parquet
{noformat}
Bug: Char and Varchar fail in MetaOperation of ThriftServer
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 23/Dec/20 16:52
Affects Version/s: 3.1.0


{code:java}
Caused by: java.lang.IllegalArgumentException: Unrecognized type name: CHAR(10)
	at org.apache.spark.sql.hive.thriftserver.SparkGetColumnsOperation.toJavaSQLType(SparkGetColumnsOperation.scala:187)
	at org.apache.spark.sql.hive.thriftserver.SparkGetColumnsOperation.$anonfun$addToRowSet$1(SparkGetColumnsOperation.scala:203)
	at scala.collection.immutable.List.foreach(List.scala:392)
	at org.apache.spark.sql.hive.thriftserver.SparkGetColumnsOperation.addToRowSet(SparkGetColumnsOperation.scala:195)
	at org.apache.spark.sql.hive.thriftserver.SparkGetColumnsOperation.$anonfun$runInternal$4(SparkGetColumnsOperation.scala:99)
	at org.apache.spark.sql.hive.thriftserver.SparkGetColumnsOperation.$anonfun$runInternal$4$adapted(SparkGetColumnsOperation.scala:98)
	at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
	at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
	at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
{code}
Bug: UnresolvedTable should retain SQL text position
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 23/Dec/20 06:24
Affects Version/s: 3.2.0

UnresolvedTable should retain SQL text position. The following commands will be handled:

"MSCK REPAIR TABLE t"
"LOAD DATA LOCAL INPATH 'filepath' INTO TABLE t"
"TRUNCATE TABLE t"
"SHOW PARTITIONS t"
"ALTER TABLE t RECOVER PARTITIONS"
"ALTER TABLE t ADD PARTITION (p=1)"
"ALTER TABLE t PARTITION (p=1) RENAME TO PARTITION (p=2)"
"ALTER TABLE t DROP PARTITION (p=1)"
"ALTER TABLE t SET SERDEPROPERTIES ('a'='b')"
"COMMENT ON TABLE t IS 'hello'"
Bug: NPE from SHOW PARTITIONS for null partition values
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 23/Dec/20 08:22
Affects Version/s: 3.2.0

The following test fails with NPE on V2 table:
{code:scala}
  test("null and empty string as partition values") {
    import testImplicits._
    withNamespaceAndTable("ns", "tbl") { t =>
      val df = Seq((0, ""), (1, null)).toDF("a", "part")
      df.write
        .partitionBy("part")
        .format("parquet")
        .mode(SaveMode.Overwrite)
        .saveAsTable(t)

      runShowPartitionsSql(s"SHOW PARTITIONS $t", Row("part=null") :: Nil)
    }
  }
{code}
{code:java}
java.lang.NullPointerException was thrown.
java.lang.NullPointerException
	at org.apache.spark.sql.execution.datasources.v2.ShowPartitionsExec.$anonfun$run$3(ShowPartitionsExec.scala:58)
	at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:238)
{code}
Bug: Correlated subquery field resolve bug when inner table has a field with the same name with outer table
Status: In Progress | Priority: Major | Resolution: 
Created: 18/Dec/20 08:58
Affects Version/s: 2.4.3

For the following statements:
{code:java}
create table t1(id int, name string)
create table id(key int, name string);

select * from id where name in (select name from t1 where t1.id = id.key)
{code}
This query is supported by spark, but since table t1 also has an attribute named id, spark will raise an error like:
{noformat}
Can't extract value from id#83: need struct type but got int;
{noformat}
According to implementation in Spark's ResolveReference rule, the name resolving precedence is `db.table` => `table.attr` => `attr.innerfield`

So here id.key should be resolved as table id's attribute, rather than the innerfield of the attribute of table t1

The problem in this resolving bug lies in the subquery resolving. In ResolveSubquery rule, we first try to resolve subquery independently, and only when there're unresolved attribute in subquery, we try resolveOuterReferences.
Bug: Unify v1 and v2 ALTER TABLE .. RENAME PARTITION tests
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 20/Dec/20 09:17
Affects Version/s: 3.2.0

Extract ALTER TABLE .. RENAME PARTITION tests to the common place to run them for V1 and v2 datasources. Some tests can be places to V1 and V2 specific test suites.
Bug: Documents a column list of INSERT in the SQL documents
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 22/Dec/20 06:53
Affects Version/s: 3.1.0

We support a column list of INSERT for Spark v3.1.0 (See: SPARK-32976 (https://github.com/apache/spark/pull/29893)). So, this ticket targets at documenting it in the SQL documents.
Bug: Add spark job maximum created files limit configuration
Status: Resolved | Priority: Major | Resolution: Duplicate
Created: 20/Dec/20 03:11
Affects Version/s: 2.4.3

Add a configuration item like : [hive.exec.max.created.files|https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties#ConfigurationProperties-hive.exec.max.created.files] to limit maximum number of HDFS files created by a single spark job.

 

This is useful when dynamic partition insertion is enabled, or for those jobs contains only 1 stage with very large parallelism
Bug: Replace Hadoop profile names by using hadoop-2 and hadoop-3
Status: In Progress | Priority: Major | Resolution: 
Created: 22/Dec/20 18:13
Affects Version/s: 3.2.0

Currently we use Hadoop profile names {{hadoop-3.2}} and {{hadoop-2.7}}. However as we evolve and upgrade these versions, such as upgrading Hadoop version to 3.3.1, these will become misleading. This propose to replace the profile names to {{hadoop-3}} and {{hadoop-2}} respectively.
Bug: Add length-check for reading char/varchar from tables w/ a external location
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 22/Dec/20 03:12
Affects Version/s: 3.1.0

spark-sql> INSERT INTO t2 VALUES ('1', 'b12345');
Time taken: 0.141 seconds
spark-sql> alter table t set location '/tmp/hive_one/t2';
Time taken: 0.095 seconds
spark-sql> select * from t;
1	b1234
1	a
1	b

the above case should fail rather than implicitly applying truncation
Bug: The test "SPARK-33305: DROP TABLE should also invalidate cache" fails for v1 Hive catalog
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 22/Dec/20 07:01
Affects Version/s: 3.2.0

The test "SPARK-33305: DROP TABLE should also invalidate cache" fails with the exception:
{code:java}
 The test fails in Hive External catalog with:
 org.apache.spark.sql.AnalysisException:
   spark_catalog.ns.tbl is not a valid TableIdentifier as it has more than 2 name parts.
{code}