av9ash/GitBugs huggingface dataset:
Bug: Improved error messages for applyInPandas for schema mismatch
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 12/Oct/22 14:37
Affects Version/s: 3.4.0

Error messages raised by `applyInPandas` and `mapInPadnas` are very generic or useless when used with complex schemata:

{code}
KeyError: 'val'
{code}
{code}
RuntimeError: Number of columns of the returned pandas.DataFrame doesn't match specified schema. Expected: 2 Actual: 3
{code}
{code}
java.lang.IllegalArgumentException: not all nodes and buffers were consumed. nodes: [ArrowFieldNode [length=3, nullCount=0]] buffers: [ArrowBuf[304], address:139860828549160, length:0, ArrowBuf[305], address:139860828549160, length:24]
{code}
{code}
pyarrow.lib.ArrowTypeError: Expected a string or bytes dtype, got int64
{code}
{code}
pyarrow.lib.ArrowInvalid: Could not convert '0' with type str: tried to convert to double
{code}

These should be improved by adding column names or descriptive messages (in the same order as above):

{code}
RuntimeError: Column names of the returned pandas.DataFrame do not match specified schema.  Missing: val  Unexpected: v  Schema: id, val
{code}
{code}
RuntimeError: Column names of the returned pandas.DataFrame do not match specified schema.  Missing: val  Unexpected: foo, v  Schema: id, val
{code}
{code}
RuntimeError: Column names of the returned pandas.DataFrame do not match specified schema.  Unexpected: v  Schema: id, id
{code}
{code}
pyarrow.lib.ArrowTypeError: Expected a string or bytes dtype, got int64
The above exception was the direct cause of the following exception:
TypeError: Exception thrown when converting pandas.Series (int64) with name 'val' to Arrow Array (string).
{code}
{code}
pyarrow.lib.ArrowInvalid: Could not convert '0' with type str: tried to convert to double
The above exception was the direct cause of the following exception:
ValueError: Exception thrown when converting pandas.Series (object) with name 'val' to Arrow Array (double).
{code}


When no column names are given, the following error was returned:
{code}
RuntimeError: Number of columns of the returned pandas.DataFrame doesn't match specified schema. Expected: 2 Actual: 3
{code}

Where it should contain the output schema:
{code}
RuntimeError: Number of columns of the returned pandas.DataFrame doesn't match specified schema.  Expected: 2  Actual: 3  Schema: id, val
{code}
Bug: Support data masking built-in Function  'mask'
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 07/Oct/22 00:09
Affects Version/s: 3.4.0

Support data masking built-in Function  *mask*

Return a masked version of str. By default, upper case letters should be converted to "X", lower case letters should be converted to "x" and numbers should be converted to "n". For example mask("abcd-EFGH-8765-4321") results in xxxx-XXXX-nnnn-nnnn. Should be able override the characters used in the mask by supplying additional arguments: the second argument controls the mask character for upper case letters, the third argument for lower case letters and the fourth argument for numbers. For example, mask("abcd-EFGH-8765-4321", "U", "l", "#") should result in llll-UUUU-####-####
Bug: Fix wrong remained shuffles log in BlockManagerDecommissioner
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 02/Oct/22 14:58
Affects Version/s: 3.3.0

 BlockManagerDecommissioner should log correct remained shuffles.
{code:java}
4 of 24 local shuffles are added. In total, 24 shuffles are remained.
2022-09-30 17:42:15.035 PDT
0 of 24 local shuffles are added. In total, 24 shuffles are remained.
2022-09-30 17:42:45.069 PDT
0 of 24 local shuffles are added. In total, 24 shuffles are remained.{code}
Bug: pyspark.pandas.read_csv supports reading multiple files, but that is undocumented
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 26/Oct/22 13:46
Affects Version/s: 3.3.1

The {{path}} argument of {{pyspark.pandas.read_csv(path, ...)}} currently has type annotation {{str}} and is documented as
{code}
    path : str
        The path string storing the CSV file to be read.
{code}


The implementation however uses {{pyspark.sql.DataFrameReader.csv(path, ...)}} which does support multiple paths:
{code}
        path : str or list
            string, or list of strings, for input path(s),
            or RDD of Strings storing CSV rows.
{code}


Loading multiple CSV files at once is a useful feature to have and should be documented (and tested for)
Bug: Upgrade Netty from 4.1.80 to 4.1.84
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 14/Oct/22 02:58
Affects Version/s: 3.4.0

* https://netty.io/news/2022/09/08/4-1-81-Final.html
 * https://netty.io/news/2022/09/13/4-1-82-Final.html
 * https://netty.io/news/2022/10/11/4-1-84-Final.html
Bug: Exclude redundant jars from spark-protobuf-assembly jar
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 14/Oct/22 03:14
Affects Version/s: 3.4.0

spark-protobuf-assembly jar produced by sbt currently includes the following jars:

 
{code:java}
[debug] Including from cache: spark-tags_2.12-3.4.0-SNAPSHOT.jar
[debug] Including from cache: unused-1.0.0.jar
[debug] Including from cache: spark-protobuf_2.12-3.4.0-SNAPSHOT.jar
[debug] Including from cache: scala-collection-compat_2.12-2.2.0.jar
[debug] Including from cache: pmml-model-1.4.8.jar
[debug] Including from cache: protobuf-java-3.21.1.jar
[debug] Including from cache: guava-14.0.1.jar
[debug] Including from cache: scala-library-2.12.17.jar {code}
The actual needs only includes:
{code:java}
[debug] Including: spark-protobuf_2.12-3.4.0-SNAPSHOT.jar
[debug] Including from cache: protobuf-java-3.21.1.jar {code}
Bug: Optimize redundant fs operations in `CommandUtils#calculateSingleLocationSize#getPathSize` method
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 12/Oct/22 04:02
Affects Version/s: 3.4.0

{code:java}
def getPathSize(fs: FileSystem, path: Path): Long = {
      val fileStatus = fs.getFileStatus(path)
      val size = if (fileStatus.isDirectory) {
        fs.listStatus(path)
          .map { status =>
            if (isDataPath(status.getPath, stagingDir)) {
              getPathSize(fs, status.getPath)
            } else {
              0L
            }
          }.sum
      } else {
        fileStatus.getLen
      }      size
    } {code}
Change input parameter from `Path` to `FileStatus`,  there is no need to do `fs.getFileStatus(path)` after each recursive call.
Bug: Improve listFunctions in SessionCatalog
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 10/Oct/22 21:55
Affects Version/s: 3.4.0

Currently `listFunctions` gets all external functions and registered functions (built-in, temporary, and persistent functions with a specific database name).  It is not necessary to get persistent functions that match a specific database name again since we already fetched them from `externalCatalog.listFunctions`.
Bug: Upgrade scalatest related test dependencies to 3.2.14
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 06/Oct/22 08:56
Affects Version/s: 3.4.0

* scalatest:[https://github.com/scalatest/scalatest/releases/tag/release-3.2.14]
 * scalatestplus
 ** scalacheck: [https://github.com/scalatest/scalatestplus-scalacheck/releases/tag/release-3.2.14.0-for-scalacheck-1.17]
 ** mockito-4-6: [https://github.com/scalatest/scalatestplus-mockito/releases/tag/release-3.2.14.0-for-mockito-4.6]
 ** https://github.com/scalatest/scalatestplus-selenium/releases/tag/release-3.2.14.0-for-selenium-4.4
Bug: Upgrade janino to 3.1.9
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 02/Oct/22 12:05
Affects Version/s: 3.4.0

[https://github.com/janino-compiler/janino/releases/tag/v3.1.8]

https://github.com/janino-compiler/janino/releases/tag/v3.1.9
Bug: Update gson transitive dependency to 2.8.9 or later
Status: Open | Priority: Minor | Resolution: 
Created: 06/Oct/22 20:36
Affects Version/s: 3.3.0

Spark 3.3 currently ships with GSON 2.8.6 and this should be managed up to 2.8.9 or later.

Versions of GSON prior to 2.8.9 are subject to [gson#1991|https://github.com/google/gson/pull/1991] , detected and reported by several flavors of static vulnerability assessment tools, at a fairly high score because it is a deserialization of untrusted data problem.

This issue is not meant to imply any particular security problem in Spark itself.

{noformat}
[INFO] org.apache.spark:spark-network-common_2.12:jar:3.3.2-SNAPSHOT
[INFO] +- com.google.crypto.tink:tink:jar:1.6.1:compile
[INFO] |  \- com.google.code.gson:gson:jar:2.8.6:compile
{noformat}

{noformat}
[INFO] org.apache.spark:spark-hive_2.12:jar:3.3.2-SNAPSHOT
[INFO] +- org.apache.hive:hive-exec:jar:core:2.3.9:compile
[INFO] |  +- com.google.code.gson:gson:jar:2.2.4:compile
{noformat}
Bug: Fix some wrong/misleading comments in DAGSchedulerSuite
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 31/Oct/22 01:28
Affects Version/s: 3.4.0

A few comments in 

test("SPARK-25341: continuous indeterminate stage roll back")

test("SPARK-23207: cannot rollback a result stage")

are wrong or misleading. need to fix them for better readability.
Bug: Exception when handling timestamp data in PySpark Structured Streaming
Status: Open | Priority: Minor | Resolution: 
Created: 28/Oct/22 10:23
Affects Version/s: 3.3.0

I'm trying to process data that contains timestamps in PySpark "Structured Streaming" using the [foreach|https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#foreach] option. When I run the job I get a {{OSError: [Errno 22] Invalid argument}} exception in {{\pyspark\sql\types.py}} at the
{noformat}
return datetime.datetime.fromtimestamp(ts // 1000000).replace(microsecond=ts % 1000000)
{noformat}
statement.

I have boiled down my Spark job to the essentials:
{noformat}
from pyspark.sql import SparkSession

def handle_row(row):
  print(f'Processing: \{row}')

spark = (SparkSession.builder
  .appName('test.stream.tstmp.byrow')
  .getOrCreate())

data = (spark.readStream
  .option('delimiter', ',')
  .option('header', True)
  .schema('a integer, b string, c timestamp')
  .csv('data/test'))

query = (data.writeStream
  .foreach(handle_row)
  .start())

query.awaitTermination()
{noformat}
In the {{data/test}} folder I have one csv file:
{noformat}
a,b,c
1,x,1970-01-01 00:59:59.999
2,y,1999-12-31 23:59:59.999
3,z,2022-10-18 15:53:12.345
{noformat}
If I change the csv schema to {{'a integer, b string, c string'}} everything works fine and I get the expected output of
{noformat}
Processing: Row(a=1, b='x', c='1970-01-01 00:59:59.999')
Processing: Row(a=2, b='y', c='1999-12-31 23:59:59.999')
Processing: Row(a=3, b='z', c='2022-10-18 15:53:12.345')
{noformat}
Also, if I change the stream handling to [micro-batches|https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#foreachbatch] like so:
{noformat}
...
def handle_batch(df, epoch_id):
  print(f'Processing: \{df} - Epoch: \{epoch_id}')
...
query = (data.writeStream
  .foreachBatch(handle_batch)
  .start())
{noformat}
I get the expected output of
{noformat}
Processing: DataFrame[a: int, b: string, c: timestamp] - Epoch: 0
{noformat}

But "by row" handling should work with the row having the correct column data type of {{timestamp}}.

This issue also affects using the {{foreach}} sink in Structured Streaming with the Kafka data souce since Kafka events contain a timestamp
Bug: Support built-in function to truncate numbers
Status: In Progress | Priority: Minor | Resolution: 
Created: 27/Oct/22 23:00
Affects Version/s: 3.4.0

Built-in function TRUNC shall truncate numbers to the previous integer or decimal. It shall optionally accept a second integer argument to indicate the number of decimal places for rounding, in either direction. When the second argument is absent, the function rounds to the nearest whole number. When the second argument is specified, the function rounds to the nearest number with n decimal places of precision. 

trunc ( numeric ) -> numeric

trunc ( double precision ) ->  double precision

eg: 
trunc(123.4382) -> 123
trunc(-123.4382) -> -123

trunc ( v numeric, s integer ) -> numeric
Truncates v to s decimal places
eg:
trunc(123.4382, 2) -> 123.43
trunc(123.4382, 0) -> 123
trunc(123.4382, -1) -> 120
trunc(123.4382, -2) -> 100
trunc(123.4382, -3) -> 0

Reference :
PostgreSQL: https://www.postgresql.org/docs/15/functions-math.html
AWS Redshift: https://docs.aws.amazon.com/redshift/latest/dg/r_TRUNC.html
Presto : https://prestodb.io/docs/current/functions/math.html?highlight=trunc#id3
Bug: Upgrade ZSTD-JNI to 1.5.2-5
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 27/Oct/22 11:49
Affects Version/s: 3.4.0

This version start to support magicless frames:
 * [https://github.com/luben/zstd-jni/issues/151]
 * https://github.com/luben/zstd-jni/pull/235
Bug: Quick submission of drivers in tests to mesos scheduler results in dropping drivers
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 24/Oct/22 16:21
Affects Version/s: 2.4.8

Queued drivers in MesosClusterScheduler are ordered based on MesosDriverDescription - and the default ordering checks for priority, followed by submission time. For two driver submissions with same priority and if made in quick succession (such that submission time is same due to millisecond granularity of Date), this results in dropping the second MesosDriverDescription from the queuedDrivers - as driverOrdering returns 0 when comparing the descriptions. This jira fixes the more immediate issue with tests, but we do need to relook at this for mess scheduler in general later.

Currently, this affects tests - for example, in the latest VOTE for 3.3.1 [1] - and is not consistently reproducible unless on a fast machine.



[1] https://lists.apache.org/thread/jof098qxp0s6qqmt9qwv52f9665b1pjg
Bug: "sbt packageBin" fails in cygwin or other windows bash session
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 10/Oct/22 20:34
Affects Version/s: 3.3.0

 In a Windows _*SHELL*_ environment, such as _*cygwin*_ or {_}*msys2/mingw64*{_}, etc,  _*Core.settings*_ in _*project/SparkBuild.scala*_ calls the wrong _*bash.exe*_ if WSL bash is present (typically at {_}*C:\Windows*{_}), causing a build failure.  This occurs even though the proper *bash.exe* is in the _*PATH*_ ahead of _*WSL*_ bash.exe.

This is fixed by [spark PR 38228|https://github.com/apache/spark/pull/38167]

There are 3 parts to the fix, implemented in _*project/SparkBuild.scala*_ :
 * determine the absolute path of the first bash.exe in the command line. 
 * determine the build environment (e.g., Linux, Darwin, CYGWIN, MSYS2, etc.)
 * For Windows SHELL environments, the first argument to the spawned Process is changed from "bash" to the absolute path.
Bug: Spark doesn't see some Parquet columns written from r-arrow
Status: Open | Priority: Minor | Resolution: 
Created: 21/Oct/22 13:11
Affects Version/s: 3.3.0

I have a Parquet file that was created in R with the r-arrow package version 9.0.0 from Conda Forge with the write_dataset() function. It has four columns, but Spark 3.3.0 only sees two of them.

{{>>> df = spark.read.parquet('part-0.parquet')}}
{{()}}
{{>>> df.head()}}
{{Row(name='Adam', age=20.0)}}
{{>>> df.columns}}
{{['name', 'age']}}
{{>>> import pandas as pd}}
{{>>> pd.read_parquet('part-0.parquet')}}
{{           name   age   age_2      age_4}}
{{0          Adam  20.0   400.0   160000.0}}
{{1           Eve  18.0   324.0   104976.0}}
{{2           Bob  50.0  2500.0  6250000.0}}
{{3  Isolated Joe   2.0     4.0       16.0}}
{{>>> import pyarrow as pa}}
{{>>> import pyarrow.parquet as pq}}
{{>>> t = pq.read_table('part-0.parquet')}}
{{>>> t}}
{{pyarrow.Table}}
{{name: string}}
{{age: double}}
{{age_2: double}}
{{age_4: double}}
{{----}}
{{name: [["Adam","Eve","Bob","Isolated Joe"]]}}
{{age: [[20,18,50,2]]}}
{{age_2: [[400,324,2500,4]]}}
{{age_4: [[160000,104976,6250000,16]]}}
{{>>> pq.read_metadata('part-0.parquet')}}
{{<pyarrow._parquet.FileMetaData object at 0x7f13e9dee5e0>}}
{{  created_by: parquet-cpp-arrow version 9.0.0}}
{{  num_columns: 4}}
{{  num_rows: 4}}
{{  num_row_groups: 1}}
{{  format_version: 2.6}}
{{  serialized_size: 1510}}
{{>>> pq.read_metadata('part-0.parquet').schema}}
{{<pyarrow._parquet.ParquetSchema object at 0x7f13e9dc46c0>}}
{{required group field_id=-1 schema {}}
{{  optional binary field_id=-1 name (String);}}
{{  optional double field_id=-1 age;}}
{{  optional double field_id=-1 age_2;}}
{{  optional double field_id=-1 age_4;}}
{{}}}

"age_2" and "age_4" look no different from "age" based on the schema. I tried changing the names (just letters) but I still get the same behavior.

Is something wrong with my file? Is something wrong with Spark?

(I'll attach the file in a minute, I just need to figure out how.)
Bug: Upgrade dropwizard metrics from 4.2.10 to 4.2.12
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 21/Oct/22 06:51
Affects Version/s: 3.4.0

* [https://github.com/dropwizard/metrics/releases/tag/v4.2.11|https://github.com/dropwizard/metrics/releases/tag/v4.2.12]
 * https://github.com/dropwizard/metrics/releases/tag/v4.2.12
Bug: Make HeartbeatReceiver as an IsolatedRpcEndpoint
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 13/Oct/22 03:58
Affects Version/s: 3.3.0

All RpcEndpoint including HeartbeatReceiver in driver are sharing one thread pool. When there're lots of rpc messages queued, the waiting process time of heartbeat time could easily exceed heartbeat timeout. Make HeartbeatReceiver extends IsolatedRcpEndpoint then it has dedicated single thread to process heartbeat
Bug: KafkaMicroBatchV2SourceWithAdminSuite failed
Status: Open | Priority: Minor | Resolution: 
Created: 10/Oct/22 14:28
Affects Version/s: 3.4.0

- ensure stream-stream self-join generates only one offset in log and correct metrics *** FAILED ***

- read Kafka transactional messages: read_committed *** FAILED ***

Failure reason to be supplemented
Bug: Fix bug in test case for catalog directory operation
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 11/Oct/22 14:28
Affects Version/s: 3.4.0

The implementation class of ExternalCatalog will perform folder operations when performing operations such as create/drop database/table/partition. The test case creates a folder in advance when obtaining the DB/Partition path URI, resulting in the result of the test case is not convincing enough force.
Bug: Dataset.groupBy.as should be preferred over Dataset.groupByKey
Status: In Progress | Priority: Minor | Resolution: 
Created: 18/Oct/22 07:43
Affects Version/s: 3.4.0

Calling {{Dataset.groupBy(...).as[K, T]}} should be preferred over calling {{Dataset.groupByKey(...)}} whenever possible. The former allows Catalyst to exploit existing partitioning and ordering of the Dataset, while the latter hides from Catalyst which columns are used to create the keys.

Example:

Calling {{ds.groupByKey(_.id)}} hides from Catalyst that column id is the grouping key.
With {{ds.groupBy($"id").as[Int, V]}} tells Catalyst that {{ds}} is to be grouped by (partitioned and ordered by) column "id".

This fact should be documented. Further, {{groupByKey}} methods with {{Column}} and {{String}} arguments would help to short cut {{groupByKey.as}} and avoid the {{groupBy(func)}} methods.
Bug: "RocksDB: commit - pause bg time total" metric always 0
Status: Open | Priority: Minor | Resolution: 
Created: 15/Oct/22 18:23
Affects Version/s: 3.3.0

{{RocksDBStateStore}} uses [pauseBg|https://github.com/apache/spark/blob/v3.3.0/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/state/RocksDBStateStoreProvider.scala#L131] key to report "RocksDB: commit - pause bg time" metric while {{RocksDB}} uses [pause|https://github.com/apache/spark/blob/v3.3.0/sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/state/RocksDB.scala#L308] to provide a value every commit. That leads to a name mismatch and 0 reported (as the default value for no metrics).
Bug: Support data masking built-in function 'mask_hash'
Status: In Progress | Priority: Minor | Resolution: 
Created: 07/Oct/22 00:26
Affects Version/s: 3.4.0

Support data masking built-in function '{*}mask_hash{*}'

Return a hashed value based on str. The hash should be consistent and should be used to join masked string values together across tables.
Bug: SparkHadoopUtil to set origin of hadoop/hive config options
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 03/Oct/22 16:24
Affects Version/s: 3.3.2

Hadoop's Configuration class allows the caller to declare the origin of an option in the {{set(key, value, source)}} method.

Having spark do this for all programmatically set values helps people trying to debug where values came from. This is particularly relevant when looking at AWS env var propagation.
Bug: ShowCreateTableSuite test failed
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 10/Oct/22 14:23
Affects Version/s: 3.4.0

ShowCreateTableSuite:
 * SHOW CREATE TABLE using Hive V1 catalog V1 command: hive table with serde info *** FAILED ***
 * - SHOW CREATE TABLE using Hive V1 catalog V2 command: hive table with serde info *** FAILED ***

[https://github.com/apache/spark/blob/1cb8250d166f5d877f24f6ea097d27d7168ecf15/sql/core/src/main/scala/org/apache/spark/sql/execution/command/tables.scala#L1345-L1347]

The iteration of `metadata.storage.properties` needs to be orderly
Bug: Update kryo transitive dependency to 5.2.0 or later
Status: Open | Priority: Minor | Resolution: 
Created: 06/Oct/22 21:04
Affects Version/s: 3.3.0

Spark 3.3 currently ships with kryo-shaded-4.0.2.jar, subject to [kryo#829|[https://github.com/EsotericSoftware/kryo/issues/829],] detected by several flavors of static vulnerability assessment tools, as a medium scored problem. 

Kryo versions 5.2.0 or later have a fix for this issue.

{noformat}
[INFO] org.apache.spark:spark-unsafe_2.12:jar:3.3.2-SNAPSHOT
[INFO] +- com.twitter:chill_2.12:jar:0.10.0:compile
[INFO] |  \- com.esotericsoftware:kryo-shaded:jar:4.0.2:compile
{noformat}

{noformat}
[INFO] org.apache.spark:spark-core_2.12:jar:3.3.2-SNAPSHOT
[INFO] +- com.twitter:chill_2.12:jar:0.10.0:compile
[INFO] |  \- com.esotericsoftware:kryo-shaded:jar:4.0.2:compile
{noformat}

This issue is not meant to imply a security problem in Spark itself.
Bug: upgrade sbt-assembly plugin to 1.2.0
Status: Resolved | Priority: Minor | Resolution: Fixed
Created: 08/Oct/22 13:05
Affects Version/s: 3.4.0

* [https://github.com/sbt/sbt-assembly/releases/tag/v1.0.0]
 * https://github.com/sbt/sbt-assembly/releases/tag/v1.1.0
 * https://github.com/sbt/sbt-assembly/releases/tag/v1.2.0
Bug: Support data masking built-in function 'mask_show_last_n'
Status: Open | Priority: Minor | Resolution: 
Created: 07/Oct/22 00:23
Affects Version/s: 3.4.0

Support data masking built-in function '{*}mask_show_last_n{*}'

Return a masked version of str, showing the last n characters unmasked. Upper case letters should be converted to "X", lower case letters should be converted to "x" and numbers should be converted to "n". For example, mask_show_last_n("1234-5678-8765-4321", 4) results in nnnn-nnnn-nnnn-4321.
Bug: Support data masking built-in function 'mask_show_first_n'
Status: Open | Priority: Minor | Resolution: 
Created: 07/Oct/22 00:19
Affects Version/s: 3.4.0

Support data masking built-in function '{*}mask_show_first_n{*}'

Return a masked version of str, showing the first n characters unmasked . Upper case letters should be converted to "X", lower case letters should be converted to "x" and numbers should be converted to "n". For example, mask_show_first_n("1234-5678-8765-4321", 4) results in 1234-nnnn-nnnn-nnnn.