This is an automated email from the ASF dual-hosted git repository.

htowaileb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new b0919af  [ASTERIXDB-2667][FUN] Share code base between string functions
b0919af is described below

commit b0919afbf42e7a7cf942d1b0f6d6d29b178b34fd
Author: Hussain Towaileb <hussain.towai...@couchbase.com>
AuthorDate: Mon Dec 9 13:55:39 2019 +0300

    [ASTERIXDB-2667][FUN] Share code base between string functions
    
    - user model changes: no
    - storage format changes: no
    - interface changes: no
    
    Details:
    - String functions concat, string_concat and string_join
      now share the same code instead of each having a different
      evaluator class.
    - Added test cases for string_join function.
    - Added support to deep missing/null checking in list
      items. Now it's possible to give a deeper (list items)
      missing value a higher priority over null arguments
      outside a list between parameters.
      Example:
      somefun([1, missing], null);
      It's possible to deeply check the list items first,
      and hence return a missing, or only do first level check
      and output would be null.
    
    Change-Id: I41b644c6841b222d1c6c529b2f9189f42178e28c
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/4023
    Contrib: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
    Integration-Tests: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Hussain Towaileb <hussai...@gmail.com>
    Reviewed-by: Ali Alsuliman <ali.al.solai...@gmail.com>
---
 .../000/concat.1.ddl.sqlpp}                        |   0
 .../000/concat.2.update.sqlpp}                     |   0
 .../000/concat.3.query.sqlpp}                      |   0
 .../001/concat.1.ddl.sqlpp}                        |   0
 .../001/concat.2.update.sqlpp}                     |   0
 .../001/concat.3.query.sqlpp}                      |   0
 .../002/concat.1.ddl.sqlpp}                        |   0
 .../002/concat.2.update.sqlpp}                     |   0
 .../002/concat.3.query.sqlpp}                      |   0
 .../003/concat.0.query.sqlpp}                      |  12 +-
 .../004/concat.1.ddl.sqlpp}                        |   0
 .../004/concat.2.update.sqlpp}                     |   0
 .../004/concat.3.query.sqlpp}                      |   0
 .../005/concat.1.query.sqlpp}                      |   0
 .../005/concat.2.query.sqlpp}                      |   0
 .../006/concat.1.ddl.sqlpp}                        |   0
 .../006/concat.2.update.sqlpp}                     |   0
 .../006/concat.3.query.sqlpp}                      |   0
 .../007/concat.1.ddl.sqlpp}                        |   0
 .../007/concat.2.update.sqlpp}                     |   0
 .../007/concat.3.query.sqlpp}                      |   0
 .../008/concat.1.query.sqlpp}                      |   0
 .../concat_pipe/concat_pipe.1.query.sqlpp          |   0
 .../concat_pipe_multi.1.query.sqlpp                |   0
 .../000/join.000.ddl.sqlpp}                        |   9 +-
 .../000/join.001.update.sqlpp}                     |  19 +-
 .../000/join.002.query.sqlpp}                      |  20 +-
 .../000/join.003.ddl.sqlpp}                        |   2 +
 .../001/join.000.ddl.sqlpp}                        |   9 +-
 .../string/join/001/join.001.update.sqlpp          |  34 ++
 .../001/join.002.query.sqlpp}                      |   7 +-
 .../001/join.003.ddl.sqlpp}                        |   2 +
 .../002/join.000.query.sqlpp}                      |  13 +-
 .../003/join.000.query.sqlpp}                      |  19 +-
 .../004/join.000.query.sqlpp}                      |  12 +-
 .../005/join.1.ddl.sqlpp}                          |   0
 .../005/join.2.update.sqlpp}                       |   0
 .../005/join.3.query.sqlpp}                        |   0
 .../concat_01.1.adm => concat/000/concat.1.adm}    |   0
 .../concat_02.1.adm => concat/001/concat.1.adm}    |   0
 .../concat_03.1.adm => concat/002/concat.1.adm}    |   0
 .../results/string/concat/003/concat.1.adm         |   1 +
 .../004/concat.1.adm}                              |   0
 .../005/concat.1.adm}                              |   0
 .../005/concat.2.adm}                              |   0
 .../strconcat01.1.adm => concat/006/concat.1.adm}  |   0
 .../strconcat02.1.adm => concat/007/concat.1.adm}  |   0
 .../concat_pipe_multi/concat_pipe_multi.1.adm      |   0
 .../runtimets/results/string/join/000/join.002.adm |   1 +
 .../runtimets/results/string/join/001/join.002.adm |  14 +
 .../runtimets/results/string/join/002/join.000.adm |   1 +
 .../runtimets/results/string/join/003/join.000.adm |   1 +
 .../runtimets/results/string/join/004/join.000.adm |   1 +
 .../string-join1.1.adm => join/005/join.1.adm}     |   0
 .../test/resources/runtimets/testsuite_sqlpp.xml   | 119 ++++---
 .../typecomputer/impl/StringJoinTypeComputer.java  |   2 +-
 .../functions/AbstractConcatStringEval.java        | 376 +++++++++++++++++++++
 .../evaluators/functions/PointableHelper.java      |  98 +++++-
 .../functions/StringConcatDescriptor.java          | 124 ++-----
 .../evaluators/functions/StringJoinDescriptor.java | 131 ++-----
 60 files changed, 723 insertions(+), 304 deletions(-)

diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/000/concat.1.ddl.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.1.ddl.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/000/concat.1.ddl.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/000/concat.2.update.sqlpp
similarity index 100%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/000/concat.2.update.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/000/concat.3.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.3.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/000/concat.3.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_02/concat_02.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/001/concat.1.ddl.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_02/concat_02.1.ddl.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/001/concat.1.ddl.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_02/concat_02.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/001/concat.2.update.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_02/concat_02.2.update.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/001/concat.2.update.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_02/concat_02.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/001/concat.3.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_02/concat_02.3.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/001/concat.3.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/002/concat.1.ddl.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.1.ddl.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/002/concat.1.ddl.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/002/concat.2.update.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.2.update.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/002/concat.2.update.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/002/concat.3.query.sqlpp
similarity index 100%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/002/concat.3.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/003/concat.0.query.sqlpp
similarity index 80%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/003/concat.0.query.sqlpp
index 21479a2..3c15a55 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/003/concat.0.query.sqlpp
@@ -17,6 +17,12 @@
  * under the License.
  */
 
-drop  dataverse test if exists;
-create  dataverse test;
-
+-- param max-warnings:json=1000
+[
+concat(),
+concat("1", "2", "3"),
+concat(1, 2, missing) is missing,
+concat(1, null, missing) is missing,
+concat(1, 2, 3) is null,
+concat(1, 2, null) is null
+];
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/004/concat.1.ddl.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.1.ddl.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/004/concat.1.ddl.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/004/concat.2.update.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.2.update.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/004/concat.2.update.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/004/concat.3.query.sqlpp
similarity index 100%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/004/concat.3.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat2/string-concat2.1.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/005/concat.1.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat2/string-concat2.1.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/005/concat.1.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat2/string-concat2.2.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/005/concat.2.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat2/string-concat2.2.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/005/concat.2.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat01/strconcat01.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/006/concat.1.ddl.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat01/strconcat01.1.ddl.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/006/concat.1.ddl.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat01/strconcat01.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/006/concat.2.update.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat01/strconcat01.2.update.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/006/concat.2.update.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat01/strconcat01.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/006/concat.3.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat01/strconcat01.3.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/006/concat.3.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat02/strconcat02.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/007/concat.1.ddl.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat02/strconcat02.2.update.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/007/concat.1.ddl.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat02/strconcat02.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/007/concat.2.update.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat02/strconcat02.1.ddl.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/007/concat.2.update.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat02/strconcat02.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/007/concat.3.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/strconcat02/strconcat02.3.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/007/concat.3.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_func/concat_func.1.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/008/concat.1.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_func/concat_func.1.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/008/concat.1.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_pipe/concat_pipe.1.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/concat_pipe/concat_pipe.1.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_pipe/concat_pipe.1.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/concat_pipe/concat_pipe.1.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_pipe_multi/concat_pipe_multi.1.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/concat_pipe_multi/concat_pipe_multi.1.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_pipe_multi/concat_pipe_multi.1.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat/concat_pipe_multi/concat_pipe_multi.1.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.000.ddl.sqlpp
similarity index 79%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.000.ddl.sqlpp
index 2a3725d..d5c4128 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.000.ddl.sqlpp
@@ -17,7 +17,14 @@
  * under the License.
  */
 
+drop dataverse test if exists;
+create dataverse test;
 use test;
 
+drop type test if exists;
+create type test as open { id: uuid, f0: int64 };
+
+drop dataset test if exists;
+
+create dataset test(test) primary key id autogenerated;
 
-{'result1':test.`string-concat`(['aa','25991','bb','31526'])};
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.001.update.sqlpp
similarity index 61%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.001.update.sqlpp
index e947647..b72db45 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.001.update.sqlpp
@@ -16,16 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description    : Test concat-string function with nulls in the list which 
is passed as an argument.
- * Success        : Yes
- */
 
 use test;
-
-
-with  k as [{'a':1,'b':'hello'},{'a':2,'b':{'k':[1,2,2]}}]
-select element test.`string-concat`([x.b,' world'])
-from  k as x
-where (x.a = 1)
-;
+insert into test({"f0": 1, "f1": ["1", "2", "3"], "f2": "-"});
+insert into test({"f0": 2, "f1": ["1", "2", "3", "4"], "f2": "-"});
+insert into test({"f0": 3, "f1": ["1", "2", "3", "4"], "f2": "|-|"});
+insert into test({"f0": 4, "f1": ["single"], "f2": "-"});
+insert into test({"f0": 5, "f1": {{"1", "2", "3"}}, "f2": "-"});
+insert into test({"f0": 6, "f1": {{"1", "2", "3", "4"}}, "f2": "-"});
+insert into test({"f0": 7, "f1": {{"1", "2", "3", "4"}}, "f2": "|-|"});
+insert into test({"f0": 8, "f1": {{"single"}}, "f2": "-"});
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.002.query.sqlpp
similarity index 61%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.002.query.sqlpp
index e947647..2163842 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_03/concat_03.3.query.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.002.query.sqlpp
@@ -16,16 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description    : Test concat-string function with nulls in the list which 
is passed as an argument.
- * Success        : Yes
- */
 
 use test;
 
-
-with  k as [{'a':1,'b':'hello'},{'a':2,'b':{'k':[1,2,2]}}]
-select element test.`string-concat`([x.b,' world'])
-from  k as x
-where (x.a = 1)
-;
+select value [
+(select value string_join(f1, f2) from test where f0 = 1)[0],
+(select value string_join(f1, f2) from test where f0 = 2)[0],
+(select value string_join(f1, f2) from test where f0 = 3)[0],
+(select value string_join(f1, f2) from test where f0 = 4)[0],
+(select value string_join(f1, f2) from test where f0 = 5)[0],
+(select value string_join(f1, f2) from test where f0 = 6)[0],
+(select value string_join(f1, f2) from test where f0 = 7)[0],
+(select value string_join(f1, f2) from test where f0 = 8)[0]
+];
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.003.ddl.sqlpp
similarity index 96%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.003.ddl.sqlpp
index bd244d0..5c1e653 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/000/join.003.ddl.sqlpp
@@ -17,3 +17,5 @@
  * under the License.
  */
 
+drop dataverse test if exists;
+
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.000.ddl.sqlpp
similarity index 79%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.000.ddl.sqlpp
index 2a3725d..d5c4128 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.000.ddl.sqlpp
@@ -17,7 +17,14 @@
  * under the License.
  */
 
+drop dataverse test if exists;
+create dataverse test;
 use test;
 
+drop type test if exists;
+create type test as open { id: uuid, f0: int64 };
+
+drop dataset test if exists;
+
+create dataset test(test) primary key id autogenerated;
 
-{'result1':test.`string-concat`(['aa','25991','bb','31526'])};
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.001.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.001.update.sqlpp
new file mode 100644
index 0000000..23f9c2b
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.001.update.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use test;
+insert into test([{ "f0": 1, "f1": ["1"], "f2": 1}]);
+insert into test([{ "f0": 2, "f1": ["1"], "f2": missing}]);
+insert into test([{ "f0": 3, "f1": ["1", "2"], "f2": null}]);
+insert into test([{ "f0": 4, "f1": ["1", 1], "f2": "-"}]);
+insert into test([{ "f0": 5, "f1": ["1", missing], "f2": "-"}]);
+insert into test([{ "f0": 6, "f1": ["1", missing, null], "f2": "-"}]);
+insert into test([{ "f0": 7, "f1": ["1", null], "f2": "-"}]);
+insert into test([{ "f0": 8, "f1": {{"1"}}, "f2": 1}]);
+insert into test([{ "f0": 9, "f1": {{"1"}}, "f2": missing}]);
+insert into test([{ "f0": 10, "f1": {{"1", "2"}}, "f2": null}]);
+insert into test([{ "f0": 11, "f1": {{"1", 1}}, "f2": "-"}]);
+insert into test([{ "f0": 12, "f1": {{"1", missing}}, "f2": "-"}]);
+insert into test([{ "f0": 13, "f1": {{"1", missing, null}}, "f2": "-"}]);
+insert into test([{ "f0": 14, "f1": {{"1", null}}, "f2": "-"}]);
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.002.query.sqlpp
similarity index 85%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.002.query.sqlpp
index 2a3725d..553f32f 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-concat1/string-concat1.3.query.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.002.query.sqlpp
@@ -17,7 +17,10 @@
  * under the License.
  */
 
-use test;
+// param max-warnings:json=1000
 
+use test;
 
-{'result1':test.`string-concat`(['aa','25991','bb','31526'])};
+select value [string_join(f1, f2) is missing, string_join(f1, f2) is null]
+from test
+order by f0 asc;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.003.ddl.sqlpp
similarity index 96%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.003.ddl.sqlpp
index bd244d0..5c1e653 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.2.update.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/001/join.003.ddl.sqlpp
@@ -17,3 +17,5 @@
  * under the License.
  */
 
+drop dataverse test if exists;
+
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/002/join.000.query.sqlpp
similarity index 72%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/002/join.000.query.sqlpp
index 21479a2..d3f00a3 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/002/join.000.query.sqlpp
@@ -17,6 +17,13 @@
  * under the License.
  */
 
-drop  dataverse test if exists;
-create  dataverse test;
-
+[
+string_join(["1", "2", "3"], "-"),
+string_join(["1", "2", "3", "4"], "-"),
+string_join(["1", "2", "3", "4"], "|-|"),
+string_join(["single"], "-"),
+string_join({{"1", "2", "3"}}, "-"),
+string_join({{"1", "2", "3", "4"}}, "-"),
+string_join({{"1", "2", "3", "4"}}, "|-|"),
+string_join({{"single"}}, "-")
+];
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/003/join.000.query.sqlpp
similarity index 56%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/003/join.000.query.sqlpp
index 21479a2..002cc78 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/003/join.000.query.sqlpp
@@ -17,6 +17,21 @@
  * under the License.
  */
 
-drop  dataverse test if exists;
-create  dataverse test;
+// param max-warnings:json=1000
 
+[
+string_join(["1"], 1) is null,
+string_join(["1"], missing) is missing,
+string_join(["1", "2"], null) is null,
+string_join(["1", 1], "-") is null,
+string_join(["1", missing], "-") is missing,
+string_join(["1", missing, null], "-") is missing,
+string_join(["1", null], "-") is null,
+string_join({{"1"}}, 1) is null,
+string_join({{"1"}}, missing) is missing,
+string_join({{"1", "2"}}, null) is null,
+string_join({{"1", 1}}, "-") is null,
+string_join({{"1", missing}}, "-") is missing,
+string_join({{"1", missing, null}}, "-") is missing,
+string_join({{"1", null}}, "-") is null
+];
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/004/join.000.query.sqlpp
similarity index 74%
copy from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
copy to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/004/join.000.query.sqlpp
index 21479a2..2a70645 100644
--- 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/004/join.000.query.sqlpp
@@ -17,6 +17,14 @@
  * under the License.
  */
 
-drop  dataverse test if exists;
-create  dataverse test;
+// param max-warnings:json=1000
 
+[
+string_join([], "-"),
+string_join(["1"], "-"),
+string_join(["1", "2"], "-"),
+string_join(["1", "2"], 1) is null,
+string_join(["1", "2"], missing) is missing,
+string_join(["1", missing], null) is null,
+string_join(["1", missing], 1) is missing
+];
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/005/join.1.ddl.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.1.ddl.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/005/join.1.ddl.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.2.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/005/join.2.update.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/concat_01/concat_01.2.update.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/005/join.2.update.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.3.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/005/join.3.query.sqlpp
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/string-join1/string-join1.3.query.sqlpp
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/join/005/join.3.query.sqlpp
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_01/concat_01.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/000/concat.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_01/concat_01.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/000/concat.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_02/concat_02.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/001/concat.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_02/concat_02.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/001/concat.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_03/concat_03.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/002/concat.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_03/concat_03.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/002/concat.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/003/concat.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/003/concat.1.adm
new file mode 100644
index 0000000..7390a31
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/003/concat.1.adm
@@ -0,0 +1 @@
+[ "", "123", true, true, true, true ]
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-concat1/string-concat1.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/004/concat.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-concat1/string-concat1.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/004/concat.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-concat2/string-concat2.2.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/005/concat.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-concat2/string-concat2.2.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/005/concat.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-concat2/string-concat2.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/005/concat.2.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-concat2/string-concat2.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/005/concat.2.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/strconcat01/strconcat01.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/006/concat.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/strconcat01/strconcat01.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/006/concat.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/strconcat02/strconcat02.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/007/concat.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/strconcat02/strconcat02.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/007/concat.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_pipe_multi/concat_pipe_multi.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/concat_pipe_multi/concat_pipe_multi.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat_pipe_multi/concat_pipe_multi.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/concat/concat_pipe_multi/concat_pipe_multi.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/000/join.002.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/000/join.002.adm
new file mode 100644
index 0000000..2cfc16c
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/000/join.002.adm
@@ -0,0 +1 @@
+[ "1-2-3", "1-2-3-4", "1|-|2|-|3|-|4", "single", "1-2-3", "1-2-3-4", 
"1|-|2|-|3|-|4", "single" ]
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/001/join.002.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/001/join.002.adm
new file mode 100644
index 0000000..5a53a7d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/001/join.002.adm
@@ -0,0 +1,14 @@
+[ false, true ]
+[ true, null ]
+[ false, true ]
+[ false, true ]
+[ true, null ]
+[ true, null ]
+[ false, true ]
+[ false, true ]
+[ true, null ]
+[ false, true ]
+[ false, true ]
+[ true, null ]
+[ true, null ]
+[ false, true ]
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/002/join.000.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/002/join.000.adm
new file mode 100644
index 0000000..2cfc16c
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/002/join.000.adm
@@ -0,0 +1 @@
+[ "1-2-3", "1-2-3-4", "1|-|2|-|3|-|4", "single", "1-2-3", "1-2-3-4", 
"1|-|2|-|3|-|4", "single" ]
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/003/join.000.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/003/join.000.adm
new file mode 100644
index 0000000..2ffdc89
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/003/join.000.adm
@@ -0,0 +1 @@
+[ true, true, true, true, true, true, true, true, true, true, true, true, 
true, true ]
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/004/join.000.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/004/join.000.adm
new file mode 100644
index 0000000..b8f461a
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/004/join.000.adm
@@ -0,0 +1 @@
+[ "", "1", "1-2", true, true, true, true ]
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-join1/string-join1.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/005/join.1.adm
similarity index 100%
rename from 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/string-join1/string-join1.1.adm
rename to 
asterixdb/asterix-app/src/test/resources/runtimets/results/string/join/005/join.1.adm
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml 
b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 201ec6b..fd19cd5 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -8921,33 +8921,54 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="concat_01">
-        <output-dir compare="Text">concat_01</output-dir>
+      <compilation-unit name="concat/001">
+        <output-dir compare="Text">concat/001</output-dir>
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="concat_02">
-        <output-dir compare="Text">concat_02</output-dir>
+      <compilation-unit name="concat/002">
+        <output-dir compare="Text">concat/002</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string" check-warnings="true">
+      <compilation-unit name="concat/003">
+        <output-dir compare="Text">concat/003</output-dir>
+        <expected-warn>Type mismatch: function string-concat expects its 1st 
input parameter to be of type string, but the actual input type is bigint (in 
line 26, at column 1)</expected-warn>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string">
+      <compilation-unit name="concat/004">
+        <output-dir compare="Text">concat/004</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string">
+      <compilation-unit name="concat/005">
+        <output-dir compare="Text">concat/005</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string">
+      <compilation-unit name="concat/006">
+        <output-dir compare="Text">concat/006</output-dir>
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="concat_03">
-        <output-dir compare="Text">concat_03</output-dir>
+      <compilation-unit name="concat/007">
+        <output-dir compare="Text">concat/007</output-dir>
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="concat_pipe">
-        <output-dir compare="Text">concat_03</output-dir>
+      <compilation-unit name="concat/008">
+        <output-dir compare="Text">concat/002</output-dir>
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="concat_pipe_multi">
-        <output-dir compare="Text">concat_pipe_multi</output-dir>
+      <compilation-unit name="concat/concat_pipe">
+        <output-dir compare="Text">concat/002</output-dir>
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="concat_func">
-        <output-dir compare="Text">concat_03</output-dir>
+      <compilation-unit name="concat/concat_pipe_multi">
+        <output-dir compare="Text">concat/concat_pipe_multi</output-dir>
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
@@ -9016,6 +9037,43 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
+      <compilation-unit name="join/000">
+        <output-dir compare="Text">join/000</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string" check-warnings="true">
+      <compilation-unit name="join/001">
+        <output-dir compare="Text">join/001</output-dir>
+        <expected-warn>Type mismatch: function string-join expects its 2nd 
input parameter to be of type string, but the actual input type is bigint (in 
line 24, at column 15)</expected-warn>
+        <expected-warn>Type mismatch: function string-join expects its 1st 
input parameter to be of type array, but the actual input type is bigint (in 
line 24, at column 15)</expected-warn>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string">
+      <compilation-unit name="join/002">
+        <output-dir compare="Text">join/002</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string" check-warnings="true">
+      <compilation-unit name="join/003">
+        <output-dir compare="Text">join/003</output-dir>
+        <expected-warn>Type mismatch: function string-join expects its 2nd 
input parameter to be of type string, but the actual input type is bigint (in 
line 23, at column 1)</expected-warn>
+        <expected-warn>Type mismatch: function string-join expects its 2nd 
input parameter to be of type string, but the actual input type is bigint (in 
line 30, at column 1)</expected-warn>
+        <expected-warn>Type mismatch: function string-join expects its 1st 
input parameter to be of type array, but the actual input type is bigint (in 
line 33, at column 1)</expected-warn>
+        <expected-warn>Type mismatch: function string-join expects its 1st 
input parameter to be of type array, but the actual input type is bigint (in 
line 26, at column 1)</expected-warn>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string" check-warnings="true">
+      <compilation-unit name="join/004">
+        <output-dir compare="Text">join/004</output-dir>
+        <expected-warn>Type mismatch: function string-join expects its 2nd 
input parameter to be of type string, but the actual input type is bigint (in 
line 26, at column 1)</expected-warn>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string" check-warnings="true">
+      <compilation-unit name="join/005">
+        <output-dir compare="Text">join/005</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string">
       <compilation-unit name="length_01">
         <output-dir compare="Text">length_01</output-dir>
       </compilation-unit>
@@ -9448,26 +9506,6 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="strconcat01">
-        <output-dir compare="Text">strconcat01</output-dir>
-      </compilation-unit>
-    </test-case>
-    <test-case FilePath="string">
-      <compilation-unit name="strconcat02">
-        <output-dir compare="Text">strconcat02</output-dir>
-      </compilation-unit>
-    </test-case>
-    <test-case FilePath="string">
-      <compilation-unit name="string-concat1">
-        <output-dir compare="Text">string-concat1</output-dir>
-      </compilation-unit>
-    </test-case>
-    <test-case FilePath="string">
-      <compilation-unit name="string-concat2">
-        <output-dir compare="Text">string-concat2</output-dir>
-      </compilation-unit>
-    </test-case>
-    <test-case FilePath="string">
       <compilation-unit name="string-equal1">
         <output-dir compare="Text">string-equal1</output-dir>
       </compilation-unit>
@@ -9488,11 +9526,6 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="string">
-      <compilation-unit name="string-join1">
-        <output-dir compare="Text">string-join1</output-dir>
-      </compilation-unit>
-    </test-case>
-    <test-case FilePath="string">
       <compilation-unit name="string-to-codepoint">
         <output-dir compare="Text">string-to-codepoint</output-dir>
       </compilation-unit>
@@ -13649,22 +13682,22 @@
     <test-case FilePath="fun_return_null/string_fun" check-warnings="true">
       <compilation-unit name="string_fun_004">
         <output-dir compare="Text">string_fun_004</output-dir>
-        <expected-warn>Unsupported type: string-concat cannot process input 
type tinyint (in line 30, at column 1)</expected-warn>
+        <expected-warn>Type mismatch: function string-concat expects its 2nd 
input parameter to be of type string, but the actual input type is tinyint (in 
line 30, at column 1)</expected-warn>
         <expected-warn>Type mismatch: function substring expects its 2nd input 
parameter to be of type tinyint, smallint, integer, bigint, float or double, 
but the actual input type is string (in line 32, at column 1)</expected-warn>
         <expected-warn>Type mismatch: function substring expects its 1st input 
parameter to be of type string, but the actual input type is bigint (in line 
33, at column 1)</expected-warn>
-        <expected-warn>Unsupported type: string-concat cannot process input 
type bigint (in line 31, at column 7)</expected-warn>
+        <expected-warn>Type mismatch: function string-concat expects its 2nd 
input parameter to be of type string, but the actual input type is bigint (in 
line 31, at column 7)</expected-warn>
         <expected-warn>Type mismatch: function codepoint-to-string expects its 
1st input parameter to be of type array, but the actual input type is string 
(in line 34, at column 1)</expected-warn>
         <expected-warn>Unsupported type: codepoint-to-string cannot process 
input type string (in line 35, at column 1)</expected-warn>
 
-        <expected-warn>Unsupported type: string-concat cannot process input 
type tinyint (in line 30, at column 1)</expected-warn>
+        <expected-warn>Type mismatch: function string-concat expects its 2nd 
input parameter to be of type string, but the actual input type is tinyint (in 
line 30, at column 1)</expected-warn>
         <expected-warn>Type mismatch: function substring expects its 2nd input 
parameter to be of type tinyint, smallint, integer, bigint, float or double, 
but the actual input type is string (in line 32, at column 1)</expected-warn>
         <expected-warn>Type mismatch: function substring expects its 1st input 
parameter to be of type string, but the actual input type is bigint (in line 
33, at column 1)</expected-warn>
-        <expected-warn>Unsupported type: string-concat cannot process input 
type bigint (in line 31, at column 7)</expected-warn>
+        <expected-warn>Type mismatch: function string-concat expects its 2nd 
input parameter to be of type string, but the actual input type is bigint (in 
line 31, at column 7)</expected-warn>
         <expected-warn>Type mismatch: function codepoint-to-string expects its 
1st input parameter to be of type array, but the actual input type is string 
(in line 34, at column 1)</expected-warn>
         <expected-warn>Unsupported type: codepoint-to-string cannot process 
input type string (in line 35, at column 1)</expected-warn>
 
-        <expected-warn>Unsupported type: string-concat cannot process input 
type bigint (in line 30, at column 1)</expected-warn>
-        <expected-warn>Unsupported type: string-concat cannot process input 
type tinyint (in line 31, at column 7)</expected-warn>
+        <expected-warn>Type mismatch: function string-concat expects its 2nd 
input parameter to be of type string, but the actual input type is bigint (in 
line 30, at column 1)</expected-warn>
+        <expected-warn>Type mismatch: function string-concat expects its 2nd 
input parameter to be of type string, but the actual input type is tinyint (in 
line 31, at column 7)</expected-warn>
         <expected-warn>Type mismatch: function substring expects its 1st input 
parameter to be of type string, but the actual input type is bigint (in line 
32, at column 1)</expected-warn>
         <expected-warn>Type mismatch: function codepoint-to-string expects its 
1st input parameter to be of type array, but the actual input type is string 
(in line 33, at column 1)</expected-warn>
         <expected-warn>Unsupported type: codepoint-to-string cannot process 
input type string (in line 34, at column 1)</expected-warn>
diff --git 
a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringJoinTypeComputer.java
 
b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringJoinTypeComputer.java
index 407a991..1777fa8 100644
--- 
a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringJoinTypeComputer.java
+++ 
b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringJoinTypeComputer.java
@@ -40,7 +40,7 @@ public class StringJoinTypeComputer extends 
AbstractResultTypeComputer {
 
     @Override
     protected IAType getResultType(ILogicalExpression expr, IAType... 
strippedInputTypes) throws AlgebricksException {
-        return validArgs(strippedInputTypes) ? ASTRING : 
AUnionType.createNullableType(ASTRING);
+        return validArgs(strippedInputTypes) ? ASTRING : 
AUnionType.createUnknownableType(ASTRING);
     }
 
     private static boolean validArgs(IAType... strippedInputTypes) {
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractConcatStringEval.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractConcatStringEval.java
new file mode 100644
index 0000000..a61d96e
--- /dev/null
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractConcatStringEval.java
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.runtime.evaluators.functions;
+
+import static 
org.apache.asterix.om.types.EnumDeserializer.ATYPETAGDESERIALIZER;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.asterix.common.annotations.MissingNullInOutFunction;
+import org.apache.asterix.om.exceptions.ExceptionUtil;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.runtime.evaluators.common.ListAccessor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+import org.apache.hyracks.data.std.api.IPointable;
+import org.apache.hyracks.data.std.primitive.VoidPointable;
+import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
+import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+import org.apache.hyracks.util.string.UTF8StringUtil;
+
+/**
+ * This evaluator performs string concatenation. It allows concatenating with 
or without a separator between each
+ * concatenation token. It also can be controlled to allow strings and lists 
of strings, or list of strings only as
+ * an input, based on the provided arguments.
+ *
+ * This evaluator is used by multiple functions that allow different arguments 
setup. This information is passed
+ * to the evaluator using the {@code isAcceptListOnly} and {@code 
separatorPosition} arguments.
+ */
+@MissingNullInOutFunction
+public abstract class AbstractConcatStringEval extends AbstractScalarEval {
+
+    // Different functions provide different argument position for the 
separator, this value indicates at which
+    // position the separator resides, if NO_SEPARATOR_POSITION value is 
provided, it means this function does not use
+    // a separator
+    private final int separatorPosition;
+    static final int NO_SEPARATOR_POSITION = -1;
+
+    // Context
+    private final IEvaluatorContext ctx;
+
+    // Storage
+    final ArrayBackedValueStorage storage = new ArrayBackedValueStorage();
+    final DataOutput storageDataOutput = storage.getDataOutput();
+
+    // Evaluators
+    private final IScalarEvaluator[] evals;
+    private final IPointable[] pointables;
+    protected ListAccessor listAccessor = new ListAccessor();
+
+    private final byte[] tempLengthArray = new byte[5];
+
+    // For handling missing, null, invalid data and warnings
+    protected boolean isMissingMet = false;
+    protected boolean isReturnNull = false;
+    protected int argIndex = -1;
+    protected byte[] expectedType = null;
+    protected ATypeTag unsupportedType = null;
+
+    protected final byte[] STRING_TYPE = new byte[] { 
ATypeTag.SERIALIZED_STRING_TYPE_TAG };
+
+    public AbstractConcatStringEval(IScalarEvaluatorFactory[] args, 
IEvaluatorContext ctx,
+            SourceLocation sourceLocation, FunctionIdentifier 
functionIdentifier, int separatorPosition)
+            throws HyracksDataException {
+        super(sourceLocation, functionIdentifier);
+        this.ctx = ctx;
+        this.separatorPosition = separatorPosition;
+
+        // Evaluators and Pointables
+        pointables = new IPointable[args.length];
+        evals = new IScalarEvaluator[args.length];
+        for (int i = 0; i < args.length; i++) {
+            evals[i] = args[i].createScalarEvaluator(ctx);
+            pointables[i] = new VoidPointable();
+        }
+    }
+
+    protected abstract boolean isAcceptedType(ATypeTag typeTag);
+
+    protected abstract byte[] getExpectedType();
+
+    @Override
+    public void evaluate(IFrameTupleReference tuple, IPointable result) throws 
HyracksDataException {
+        // Reset the members
+        resetValidationVariables();
+
+        // Evaluate and check the arguments (Missing/Null checks)
+        for (int i = 0; i < evals.length; i++) {
+            evals[i].evaluate(tuple, pointables[i]);
+            isMissingMet = doMissingNullCheck(result, pointables[i]);
+            if (isMissingMet) {
+                return;
+            }
+        }
+
+        // Terminate execution if any null outer argument has been encountered
+        if (isReturnNull) {
+            PointableHelper.setNull(result);
+            return;
+        }
+
+        byte[] separatorBytes = null;
+        int separatorStartOffset = 0;
+
+        // Validate the separator type (if it is present)
+        if (separatorPosition != NO_SEPARATOR_POSITION) {
+            separatorBytes = pointables[separatorPosition].getByteArray();
+            separatorStartOffset = 
pointables[separatorPosition].getStartOffset();
+            ATypeTag separatorTypeTag = 
ATYPETAGDESERIALIZER.deserialize(separatorBytes[separatorStartOffset]);
+
+            if (separatorTypeTag != ATypeTag.STRING) {
+                isReturnNull = true;
+                argIndex = separatorPosition;
+                expectedType = STRING_TYPE;
+                unsupportedType = separatorTypeTag;
+            }
+        }
+
+        // Rest of arguments check
+        for (int i = 0; i < pointables.length; i++) {
+            // Separator can come at different positions, so when validating 
other arguments, skip separator position
+            if (i != separatorPosition) {
+                byte[] bytes = pointables[i].getByteArray();
+                int startOffset = pointables[i].getStartOffset();
+                ATypeTag typeTag = 
ATYPETAGDESERIALIZER.deserialize(bytes[startOffset]);
+
+                if (!isAcceptedType(typeTag)) {
+                    isReturnNull = true;
+                    if (unsupportedType == null) {
+                        argIndex = i;
+                        expectedType = getExpectedType();
+                        unsupportedType = typeTag;
+                    }
+                }
+
+                // If the item is a list, we are checking list elements here
+                if (typeTag.isListType()) {
+                    listAccessor.reset(bytes, startOffset);
+
+                    for (int j = 0; j < listAccessor.size(); j++) {
+                        int itemStartOffset = listAccessor.getItemOffset(j);
+                        ATypeTag itemTypeTag = 
listAccessor.getItemType(itemStartOffset);
+
+                        if (itemTypeTag != ATypeTag.STRING) {
+                            if (itemTypeTag == ATypeTag.MISSING) {
+                                PointableHelper.setMissing(result);
+                                return;
+                            }
+
+                            isReturnNull = true;
+                            if (unsupportedType == null || itemTypeTag == 
ATypeTag.NULL) {
+                                argIndex = getActualArgumentIndex(i, j);
+                                expectedType = getExpectedType();
+                                unsupportedType = itemTypeTag;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (isReturnNull) {
+            PointableHelper.setNull(result);
+            if (unsupportedType != null && unsupportedType != ATypeTag.NULL) {
+                ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, 
functionIdentifier, unsupportedType.serialize(),
+                        argIndex, expectedType);
+            }
+            return;
+        }
+
+        // Concatenate the strings
+        int utf8Length = calculateStringLength(separatorBytes, 
separatorStartOffset);
+        constructConcatenatedString(utf8Length, separatorBytes, 
separatorStartOffset);
+
+        result.set(storage);
+    }
+
+    /**
+     * Resets the validation variables to their default values at the start of 
each tuple run.
+     */
+    private void resetValidationVariables() {
+        isMissingMet = false;
+        isReturnNull = false;
+        argIndex = -1;
+        expectedType = null;
+        unsupportedType = null;
+    }
+
+    /**
+     * Performs the missing/null check.
+     *
+     * @param result result pointable
+     * @param pointable pointable to be checked
+     *
+     * @return {@code true} if execution should stop (missing encountered), 
{@code false} otherwise.
+     * @throws HyracksDataException Hyracks data exception
+     */
+    protected boolean doMissingNullCheck(IPointable result, IPointable 
pointable) throws HyracksDataException {
+        if (PointableHelper.checkAndSetMissingOrNull(result, pointable)) {
+            if (result.getByteArray()[result.getStartOffset()] == 
ATypeTag.SERIALIZED_MISSING_TYPE_TAG) {
+                return true;
+            }
+
+            // null value, but check other arguments for missing first (higher 
priority)
+            isReturnNull = true;
+        }
+        return false;
+    }
+
+    /**
+     * One of the functions using this evaluator has its arguments (multiple 
arguments) internally converted into a
+     * single argument as a list of arguments. This method can be overridden 
to use the position in the list to return
+     * the correct position of the argument when reporting back.
+     */
+    int getActualArgumentIndex(int outArgumentIndex, int innerArgumentIndex) {
+        return outArgumentIndex;
+    }
+
+    /**
+     * Calculates the required total length for the generated string result
+     *
+     * @return The total string length
+     */
+    private int calculateStringLength(final byte[] separatorBytes, final int 
separatorStartOffset)
+            throws HyracksDataException {
+        int utf8Length = 0;
+
+        // Separator length is fixed, calculate it once only (if present)
+        int separatorLength = 0;
+        if (separatorPosition != NO_SEPARATOR_POSITION) {
+            separatorLength = UTF8StringUtil.getUTFLength(separatorBytes, 
separatorStartOffset + 1);
+        }
+
+        for (int i = 0; i < pointables.length; i++) {
+            // Skip separator position
+            if (i != separatorPosition) {
+                byte[] bytes = pointables[i].getByteArray();
+                int startOffset = pointables[i].getStartOffset();
+                ATypeTag typeTag = 
ATYPETAGDESERIALIZER.deserialize(bytes[startOffset]);
+
+                // Second argument is a string or array
+                // Calculate total string length (string has variable length)
+                try {
+                    if (typeTag == ATypeTag.STRING) {
+                        startOffset++; // skip the type tag byte
+                        utf8Length += UTF8StringUtil.getUTFLength(bytes, 
startOffset);
+                    } else {
+                        listAccessor.reset(bytes, startOffset);
+
+                        for (int j = 0; j < listAccessor.size(); j++) {
+                            int itemStartOffset = 
listAccessor.getItemOffset(j);
+
+                            if (listAccessor.itemsAreSelfDescribing()) {
+                                itemStartOffset++;
+                            }
+
+                            utf8Length += UTF8StringUtil.getUTFLength(bytes, 
itemStartOffset);
+
+                            // Ensure separator is present before applying the 
separator calculation
+                            // Separator length (on last item, do not add 
separator length)
+                            if (separatorPosition != NO_SEPARATOR_POSITION && 
j < listAccessor.size() - 1) {
+                                utf8Length += separatorLength;
+                            }
+                        }
+                    }
+                } catch (IOException ex) {
+                    throw HyracksDataException.create(ex);
+                }
+
+                // Ensure separator is present before applying the separator 
calculation
+                // Separator length (on last item, do not add separator length)
+                // Depending on separator position (start or end), we need to 
adjust the calculation
+                if (separatorPosition != NO_SEPARATOR_POSITION
+                        && i < pointables.length - (separatorPosition > 0 ? 2 
: 1)) {
+                    utf8Length += separatorLength;
+                }
+            }
+        }
+
+        return utf8Length;
+    }
+
+    /**
+     * Constructs the concatenated string from the provided strings
+     */
+    private void constructConcatenatedString(final int utf8Length, final 
byte[] separatorBytes,
+            final int separatorStartOffset) throws HyracksDataException {
+        // Arguments validation is done in the length calculation step
+        try {
+            Arrays.fill(tempLengthArray, (byte) 0);
+            int cbytes = UTF8StringUtil.encodeUTF8Length(utf8Length, 
tempLengthArray, 0);
+            storage.reset();
+            storageDataOutput.writeByte(ATypeTag.SERIALIZED_STRING_TYPE_TAG);
+            storageDataOutput.write(tempLengthArray, 0, cbytes);
+
+            // Separator length is fixed, calculate it once only (if present)
+            int separatorLength = 0;
+            int separatorMetaLength = 0;
+            if (separatorPosition != NO_SEPARATOR_POSITION) {
+                separatorLength = UTF8StringUtil.getUTFLength(separatorBytes, 
separatorStartOffset + 1);
+                separatorMetaLength = 
UTF8StringUtil.getNumBytesToStoreLength(separatorLength);
+            }
+
+            int StringLength;
+            for (int i = 0; i < pointables.length; i++) {
+                // Skip separator position
+                if (i != separatorPosition) {
+                    byte[] bytes = pointables[i].getByteArray();
+                    int startOffset = pointables[i].getStartOffset();
+                    ATypeTag typeTag = 
ATYPETAGDESERIALIZER.deserialize(bytes[startOffset]);
+
+                    if (typeTag == ATypeTag.STRING) {
+                        startOffset++; // skip the type tag byte
+                        StringLength = UTF8StringUtil.getUTFLength(bytes, 
startOffset);
+                        storageDataOutput.write(bytes,
+                                
UTF8StringUtil.getNumBytesToStoreLength(StringLength) + startOffset, 
StringLength);
+                    } else {
+                        listAccessor.reset(bytes, startOffset);
+
+                        for (int j = 0; j < listAccessor.size(); j++) {
+                            int itemStartOffset = 
listAccessor.getItemOffset(j);
+
+                            if (listAccessor.itemsAreSelfDescribing()) {
+                                itemStartOffset++;
+                            }
+
+                            StringLength = UTF8StringUtil.getUTFLength(bytes, 
itemStartOffset);
+                            storageDataOutput.write(bytes,
+                                    
UTF8StringUtil.getNumBytesToStoreLength(StringLength) + itemStartOffset,
+                                    StringLength);
+
+                            // Ensure separator is present before applying the 
separator calculation
+                            // More arguments, add a separator
+                            if (separatorPosition != NO_SEPARATOR_POSITION && 
j < listAccessor.size() - 1) {
+                                storageDataOutput.write(separatorBytes, 
separatorMetaLength + separatorStartOffset + 1,
+                                        separatorLength);
+                            }
+                        }
+                    }
+
+                    // Ensure separator is present before applying the 
separator calculation
+                    // More arguments, add a separator
+                    // Depending on separator position (start or end), we need 
to adjust the calculation
+                    if (separatorPosition != NO_SEPARATOR_POSITION
+                            && i < pointables.length - (separatorPosition > 0 
? 2 : 1)) {
+                        storageDataOutput.write(separatorBytes, 
separatorMetaLength + separatorStartOffset + 1,
+                                separatorLength);
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            throw HyracksDataException.create(ex);
+        }
+    }
+}
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
index 56dd802..01aa181 100644
--- 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
@@ -18,6 +18,8 @@
  */
 package org.apache.asterix.runtime.evaluators.functions;
 
+import static 
org.apache.asterix.om.types.EnumDeserializer.ATYPETAGDESERIALIZER;
+
 import java.io.DataOutput;
 import java.io.IOException;
 import java.util.Collection;
@@ -28,6 +30,7 @@ import 
org.apache.asterix.om.pointables.base.IVisitablePointable;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.EnumDeserializer;
 import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
+import org.apache.asterix.runtime.evaluators.common.ListAccessor;
 import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.api.IMutableValueStorage;
@@ -164,20 +167,46 @@ public class PointableHelper {
         pointable.set(MISSING_BYTES, 0, MISSING_BYTES.length);
     }
 
-    // checkAndSetMissingOrNull with 1 argument
-    public static boolean checkAndSetMissingOrNull(IPointable result, 
IPointable pointable1) {
-        return checkAndSetMissingOrNull(result, pointable1, null, null, null);
+    // 1 pointable check
+    public static boolean checkAndSetMissingOrNull(IPointable result, 
IPointable pointable1)
+            throws HyracksDataException {
+        return checkAndSetMissingOrNull(result, null, pointable1, null, null, 
null);
     }
 
-    // checkAndSetMissingOrNull with 2 arguments
-    public static boolean checkAndSetMissingOrNull(IPointable result, 
IPointable pointable1, IPointable pointable2) {
-        return checkAndSetMissingOrNull(result, pointable1, pointable2, null, 
null);
+    // 2 pointables check
+    public static boolean checkAndSetMissingOrNull(IPointable result, 
IPointable pointable1, IPointable pointable2)
+            throws HyracksDataException {
+        return checkAndSetMissingOrNull(result, null, pointable1, pointable2, 
null, null);
     }
 
-    // checkAndSetMissingOrNull with 3 arguments
+    // 3 pointables check
     public static boolean checkAndSetMissingOrNull(IPointable result, 
IPointable pointable1, IPointable pointable2,
-            IPointable pointable3) {
-        return checkAndSetMissingOrNull(result, pointable1, pointable2, 
pointable3, null);
+            IPointable pointable3) throws HyracksDataException {
+        return checkAndSetMissingOrNull(result, null, pointable1, pointable2, 
pointable3, null);
+    }
+
+    // 4 pointables check
+    public static boolean checkAndSetMissingOrNull(IPointable result, 
IPointable pointable1, IPointable pointable2,
+            IPointable pointable3, IPointable pointable4) throws 
HyracksDataException {
+        return checkAndSetMissingOrNull(result, null, pointable1, pointable2, 
pointable3, pointable4);
+    }
+
+    // 1 pointable check (check list members for missing values)
+    public static boolean checkAndSetMissingOrNull(IPointable result, 
ListAccessor listAccessor, IPointable pointable1)
+            throws HyracksDataException {
+        return checkAndSetMissingOrNull(result, listAccessor, pointable1, 
null, null, null);
+    }
+
+    // 2 pointables check (check list members for missing values)
+    public static boolean checkAndSetMissingOrNull(IPointable result, 
ListAccessor listAccessor, IPointable pointable1,
+            IPointable pointable2) throws HyracksDataException {
+        return checkAndSetMissingOrNull(result, listAccessor, pointable1, 
pointable2, null, null);
+    }
+
+    // 3 pointables check (check list members for missing values)
+    public static boolean checkAndSetMissingOrNull(IPointable result, 
ListAccessor listAccessor, IPointable pointable1,
+            IPointable pointable2, IPointable pointable3) throws 
HyracksDataException {
+        return checkAndSetMissingOrNull(result, listAccessor, pointable1, 
pointable2, pointable3, null);
     }
 
     /**
@@ -188,7 +217,12 @@ public class PointableHelper {
      * As the missing encounter has a higher priority than the null, the 
method will keep checking if any missing has
      * been encountered first, if not, it will do a null check at the end.
      *
+     * If the listAccessor is passed, this method will also go through any 
list pointable elements and search for
+     * a missing value to give it a higher priority over null values. If 
{@code null} is passed for the listAccessor,
+     * the list element check will be skipped.
+     *
      * @param result the result pointable that will hold the data
+     * @param listAccessor list accessor to use for check list elements.
      * @param pointable1 the first pointable to be checked
      * @param pointable2 the second pointable to be checked
      * @param pointable3 the third pointable to be checked
@@ -196,13 +230,13 @@ public class PointableHelper {
      *
      * @return {@code true} if the pointable value is missing or null, {@code 
false} otherwise.
      */
-    public static boolean checkAndSetMissingOrNull(IPointable result, 
IPointable pointable1, IPointable pointable2,
-            IPointable pointable3, IPointable pointable4) {
+    public static boolean checkAndSetMissingOrNull(IPointable result, 
ListAccessor listAccessor, IPointable pointable1,
+            IPointable pointable2, IPointable pointable3, IPointable 
pointable4) throws HyracksDataException {
 
         // this flag will keep an eye on whether a null value is encountered 
or not
         boolean isMeetNull = false;
 
-        switch (getPointableValueState(pointable1)) {
+        switch (getPointableValueState(pointable1, listAccessor)) {
             case MISSING:
                 setMissing(result);
                 return true;
@@ -212,7 +246,7 @@ public class PointableHelper {
         }
 
         if (pointable2 != null) {
-            switch (getPointableValueState(pointable2)) {
+            switch (getPointableValueState(pointable2, listAccessor)) {
                 case MISSING:
                     setMissing(result);
                     return true;
@@ -223,7 +257,7 @@ public class PointableHelper {
         }
 
         if (pointable3 != null) {
-            switch (getPointableValueState(pointable3)) {
+            switch (getPointableValueState(pointable3, listAccessor)) {
                 case MISSING:
                     setMissing(result);
                     return true;
@@ -234,7 +268,7 @@ public class PointableHelper {
         }
 
         if (pointable4 != null) {
-            switch (getPointableValueState(pointable4)) {
+            switch (getPointableValueState(pointable4, listAccessor)) {
                 case MISSING:
                     setMissing(result);
                     return true;
@@ -257,23 +291,51 @@ public class PointableHelper {
     /**
      * This method checks and returns the pointable value state.
      *
+     * If a ListAccessor is passed to this function, it will check if the 
passed pointable is a list, and if so, it
+     * will search for a missing value inside the list before checking for 
null values. If the listAccessor value is
+     * null, no list elements check will be performed.
+     *
      * @param pointable the pointable to be checked
+     * @param listAccessor list accessor used to check the list elements.
      *
      * @return the pointable value state for the passed pointable
      */
-    private static PointableValueState getPointableValueState(IPointable 
pointable) {
+    private static PointableValueState getPointableValueState(IPointable 
pointable, ListAccessor listAccessor)
+            throws HyracksDataException {
         if (pointable.getLength() == 0) {
             return PointableValueState.EMPTY_POINTABLE;
         }
 
         byte[] bytes = pointable.getByteArray();
         int offset = pointable.getStartOffset();
+        ATypeTag typeTag = ATYPETAGDESERIALIZER.deserialize(bytes[offset]);
 
-        if (bytes[offset] == ATypeTag.SERIALIZED_MISSING_TYPE_TAG) {
+        if (typeTag == ATypeTag.MISSING) {
             return PointableValueState.MISSING;
         }
 
-        if (bytes[offset] == ATypeTag.SERIALIZED_NULL_TYPE_TAG) {
+        if (typeTag == ATypeTag.NULL) {
+            return PointableValueState.NULL;
+        }
+
+        boolean isNull = false;
+
+        // Check the list elements first as it may have a missing that needs 
to be reported first
+        if (listAccessor != null && typeTag.isListType()) {
+            listAccessor.reset(bytes, offset);
+
+            for (int i = 0; i < listAccessor.size(); i++) {
+                if (listAccessor.getItemType(listAccessor.getItemOffset(i)) == 
ATypeTag.MISSING) {
+                    return PointableValueState.MISSING;
+                }
+
+                if (listAccessor.getItemType(listAccessor.getItemOffset(i)) == 
ATypeTag.NULL) {
+                    isNull = true;
+                }
+            }
+        }
+
+        if (isNull) {
             return PointableValueState.NULL;
         }
 
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
index e16ab51..319b615 100644
--- 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
@@ -18,31 +18,16 @@
  */
 package org.apache.asterix.runtime.evaluators.functions;
 
-import java.io.DataOutput;
-import java.io.IOException;
-
 import org.apache.asterix.common.annotations.MissingNullInOutFunction;
-import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider;
-import org.apache.asterix.om.base.AMissing;
-import org.apache.asterix.om.base.ANull;
-import org.apache.asterix.om.exceptions.ExceptionUtil;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.om.types.ATypeTag;
-import org.apache.asterix.om.types.BuiltinType;
 import 
org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
-import org.apache.asterix.runtime.evaluators.common.ListAccessor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
-import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.data.std.api.IPointable;
-import org.apache.hyracks.data.std.primitive.VoidPointable;
-import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
-import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
-import org.apache.hyracks.util.string.UTF8StringUtil;
 
 @MissingNullInOutFunction
 public class StringConcatDescriptor extends 
AbstractScalarFunctionDynamicDescriptor {
@@ -57,98 +42,31 @@ public class StringConcatDescriptor extends 
AbstractScalarFunctionDynamicDescrip
 
             @Override
             public IScalarEvaluator createScalarEvaluator(final 
IEvaluatorContext ctx) throws HyracksDataException {
-                return new IScalarEvaluator() {
-
-                    private final ArrayBackedValueStorage resultStorage = new 
ArrayBackedValueStorage();
-                    private final ListAccessor listAccessor = new 
ListAccessor();
-                    private final DataOutput out = 
resultStorage.getDataOutput();
-                    private final IScalarEvaluatorFactory listEvalFactory = 
args[0];
-                    private final IPointable inputArgList = new 
VoidPointable();
-                    private final IScalarEvaluator evalList = 
listEvalFactory.createScalarEvaluator(ctx);
-                    @SuppressWarnings("unchecked")
-                    private ISerializerDeserializer<ANull> nullSerde =
-                            
SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ANULL);
-                    @SuppressWarnings("unchecked")
-                    private ISerializerDeserializer<AMissing> missingSerde =
-                            
SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.AMISSING);
-                    private final byte[] tempLengthArray = new byte[5];
-                    private final byte[] expectedTypes = new byte[] { 
ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG,
-                            ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG };
+                return new AbstractConcatStringEval(args, ctx, sourceLoc, 
getIdentifier(),
+                        AbstractConcatStringEval.NO_SEPARATOR_POSITION) {
 
                     @Override
-                    public void evaluate(IFrameTupleReference tuple, 
IPointable result) throws HyracksDataException {
-                        resultStorage.reset();
-                        try {
-                            evalList.evaluate(tuple, inputArgList);
-
-                            if 
(PointableHelper.checkAndSetMissingOrNull(result, inputArgList)) {
-                                return;
-                            }
+                    protected boolean isAcceptedType(ATypeTag typeTag) {
+                        return typeTag.isListType();
+                    }
 
-                            byte[] listBytes = inputArgList.getByteArray();
-                            int listOffset = inputArgList.getStartOffset();
+                    @Override
+                    protected byte[] getExpectedType() {
+                        return STRING_TYPE;
+                    }
 
-                            if (listBytes[listOffset] != 
ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG
-                                    && listBytes[listOffset] != 
ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG) {
-                                PointableHelper.setNull(result);
-                                ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, 
getIdentifier(), listBytes[listOffset],
-                                        0, expectedTypes);
-                                return;
-                            }
-                            listAccessor.reset(listBytes, listOffset);
-                            // calculate length first
-                            int utf8Len = 0;
-                            boolean itemIsNull = false;
-                            ATypeTag unsupportedType = null;
-                            for (int i = 0; i < listAccessor.size(); i++) {
-                                int itemOffset = listAccessor.getItemOffset(i);
-                                ATypeTag itemType = 
listAccessor.getItemType(itemOffset);
-                                // Increase the offset by 1 if the give list 
has heterogeneous elements,
-                                // since the item itself has a typetag.
-                                if (listAccessor.itemsAreSelfDescribing()) {
-                                    itemOffset += 1;
-                                }
-                                if (itemType != ATypeTag.STRING) {
-                                    if (itemType == ATypeTag.NULL) {
-                                        itemIsNull = true;
-                                        continue;
-                                    }
-                                    if (itemType == ATypeTag.MISSING) {
-                                        
missingSerde.serialize(AMissing.MISSING, out);
-                                        result.set(resultStorage);
-                                        return;
-                                    }
-                                    if (unsupportedType == null) {
-                                        unsupportedType = itemType;
-                                    }
-                                }
-                                utf8Len += 
UTF8StringUtil.getUTFLength(listBytes, itemOffset);
-                            }
-                            if (itemIsNull || unsupportedType != null) {
-                                nullSerde.serialize(ANull.NULL, out);
-                                result.set(resultStorage);
-                                if (unsupportedType != null) {
-                                    ExceptionUtil.warnUnsupportedType(ctx, 
sourceLoc, getIdentifier().getName(),
-                                            unsupportedType);
-                                }
-                                return;
-                            }
-                            out.writeByte(ATypeTag.SERIALIZED_STRING_TYPE_TAG);
-                            int cbytes = 
UTF8StringUtil.encodeUTF8Length(utf8Len, tempLengthArray, 0);
-                            out.write(tempLengthArray, 0, cbytes);
-                            for (int i = 0; i < listAccessor.size(); i++) {
-                                int itemOffset = listAccessor.getItemOffset(i);
-                                if (listAccessor.itemsAreSelfDescribing()) {
-                                    itemOffset += 1;
-                                }
-                                utf8Len = 
UTF8StringUtil.getUTFLength(listBytes, itemOffset);
-                                out.write(listBytes, 
UTF8StringUtil.getNumBytesToStoreLength(utf8Len) + itemOffset,
-                                        utf8Len);
-                            }
-                        } catch (IOException e) {
-                            throw HyracksDataException.create(e);
-                        }
-                        result.set(resultStorage);
+                    /**
+                     * This function has its arguments converted into a single 
list of arguments, the inner index
+                     * can be used to point to the correct argument for user 
perspective.
+                     *
+                     * @param outArgumentIndex outer argument index
+                     * @param innerArgumentIndex inner argument index (inside 
a list)
+                     *
+                     * @return the actual index of the current argument
+                     */
+                    @Override
+                    int getActualArgumentIndex(int outArgumentIndex, int 
innerArgumentIndex) {
+                        return innerArgumentIndex;
                     }
                 };
             }
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java
index 0c39fae..960ac5c 100644
--- 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java
@@ -18,27 +18,33 @@
  */
 package org.apache.asterix.runtime.evaluators.functions;
 
-import java.io.DataOutput;
-import java.io.IOException;
-
 import org.apache.asterix.common.annotations.MissingNullInOutFunction;
-import org.apache.asterix.om.exceptions.ExceptionUtil;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.om.types.ATypeTag;
 import 
org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
-import org.apache.asterix.runtime.evaluators.common.ListAccessor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.data.std.api.IPointable;
-import org.apache.hyracks.data.std.primitive.VoidPointable;
-import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
-import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
-import org.apache.hyracks.util.string.UTF8StringUtil;
 
+/**
+ * This function takes 2 arguments, first argument is a list of strings, and 
second argument is a string separator,
+ * the function concatenates the strings in the first list argument together 
and returns that as a result.
+ *
+ * If only a single string is passed in the list, the separator is not added 
to the result.
+ *
+ * The behavior is as follows:
+ * - If the first argument is a list of strings, and the second argument is a 
string, concatenated string is returned.
+ * - If any argument is missing, missing is returned.
+ * - If any argument is null, null is returned.
+ * - If any item is not a string, or the array is containing non-strings, null 
is returned.
+ *
+ * Examples:
+ * string_join(["1", "2"], "-") -> "1-2"
+ * string_join(["1"], "-") -> "1"
+ */
 @MissingNullInOutFunction
 public class StringJoinDescriptor extends 
AbstractScalarFunctionDynamicDescriptor {
 
@@ -52,102 +58,19 @@ public class StringJoinDescriptor extends 
AbstractScalarFunctionDynamicDescripto
 
             @Override
             public IScalarEvaluator createScalarEvaluator(final 
IEvaluatorContext ctx) throws HyracksDataException {
-                return new IScalarEvaluator() {
-                    private final ArrayBackedValueStorage resultStorage = new 
ArrayBackedValueStorage();
-                    private final ListAccessor listAccessor = new 
ListAccessor();
-                    private final DataOutput out = 
resultStorage.getDataOutput();
-                    private final IScalarEvaluatorFactory listEvalFactory = 
args[0];
-                    private final IScalarEvaluatorFactory sepEvalFactory = 
args[1];
-                    private final IPointable inputArgList = new 
VoidPointable();
-                    private final IPointable inputArgSep = new VoidPointable();
-                    private final IScalarEvaluator evalList = 
listEvalFactory.createScalarEvaluator(ctx);
-                    private final IScalarEvaluator evalSep = 
sepEvalFactory.createScalarEvaluator(ctx);
-                    private final byte[] tempLengthArray = new byte[5];
-                    private final byte[] expectedTypeList =
-                            { ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG, 
ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG };
+                return new AbstractConcatStringEval(args, ctx, sourceLoc, 
getIdentifier(), 1) {
 
-                    @Override
-                    public void evaluate(IFrameTupleReference tuple, 
IPointable result) throws HyracksDataException {
-                        resultStorage.reset();
-                        evalList.evaluate(tuple, inputArgList);
-                        evalSep.evaluate(tuple, inputArgSep);
+                    // TODO Need a way to report array of strings is the 
expected type
+                    private final byte[] ARRAY_TYPE = new byte[] { 
ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG };
 
-                        if (PointableHelper.checkAndSetMissingOrNull(result, 
inputArgList, inputArgSep)) {
-                            return;
-                        }
-
-                        byte[] listBytes = inputArgList.getByteArray();
-                        int listOffset = inputArgList.getStartOffset();
-                        if (listBytes[listOffset] != 
ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG
-                                && listBytes[listOffset] != 
ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG) {
-                            PointableHelper.setNull(result);
-                            ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, 
getIdentifier(), listBytes[listOffset], 0,
-                                    expectedTypeList);
-                            return;
-                        }
-                        byte[] sepBytes = inputArgSep.getByteArray();
-                        int sepOffset = inputArgSep.getStartOffset();
-                        if (sepBytes[sepOffset] != 
ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
-                            PointableHelper.setNull(result);
-                            ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, 
getIdentifier(), sepBytes[sepOffset], 1,
-                                    ATypeTag.STRING);
-                            return;
-                        }
-                        int sepLen = UTF8StringUtil.getUTFLength(sepBytes, 
sepOffset + 1);
-                        int sepMetaLen = 
UTF8StringUtil.getNumBytesToStoreLength(sepLen);
-
-                        listAccessor.reset(listBytes, listOffset);
-                        try {
-                            // calculate length first
-                            int utf8Len = 0;
-                            int size = listAccessor.size();
-                            for (int i = 0; i < size; i++) {
-                                int itemOffset = listAccessor.getItemOffset(i);
-                                ATypeTag itemType = 
listAccessor.getItemType(itemOffset);
-                                // Increase the offset by 1 if the give list 
has heterogeneous elements,
-                                // since the item itself has a typetag.
-                                if (listAccessor.itemsAreSelfDescribing()) {
-                                    itemOffset += 1;
-                                }
-                                if (itemType != ATypeTag.STRING) {
-                                    if (itemType == ATypeTag.MISSING) {
-                                        PointableHelper.setMissing(result);
-                                        return;
-                                    }
-                                    PointableHelper.setNull(result);
-                                    if (itemType != ATypeTag.NULL) {
-                                        // warn only if the call is: 
string_join([1,3], "/") where elements are non-null
-                                        ExceptionUtil.warnUnsupportedType(ctx, 
sourceLoc, getIdentifier().getName(),
-                                                itemType);
-                                    }
-                                    return;
-                                }
-                                int currentSize = 
UTF8StringUtil.getUTFLength(listBytes, itemOffset);
-                                if (i != size - 1 && currentSize != 0) {
-                                    utf8Len += sepLen;
-                                }
-                                utf8Len += currentSize;
-                            }
+                    @Override
+                    protected boolean isAcceptedType(ATypeTag typeTag) {
+                        return typeTag.isListType();
+                    }
 
-                            out.writeByte(ATypeTag.SERIALIZED_STRING_TYPE_TAG);
-                            int cbytes = 
UTF8StringUtil.encodeUTF8Length(utf8Len, tempLengthArray, 0);
-                            out.write(tempLengthArray, 0, cbytes);
-                            for (int i = 0; i < listAccessor.size(); i++) {
-                                int itemOffset = listAccessor.getItemOffset(i);
-                                if (listAccessor.itemsAreSelfDescribing()) {
-                                    itemOffset += 1;
-                                }
-                                utf8Len = 
UTF8StringUtil.getUTFLength(listBytes, itemOffset);
-                                out.write(listBytes, 
UTF8StringUtil.getNumBytesToStoreLength(utf8Len) + itemOffset,
-                                        utf8Len);
-                                for (int j = 0; j < sepLen; j++) {
-                                    out.writeByte(sepBytes[sepOffset + 1 + 
sepMetaLen + j]);
-                                }
-                            }
-                        } catch (IOException ex) {
-                            throw HyracksDataException.create(ex);
-                        }
-                        result.set(resultStorage);
+                    @Override
+                    protected byte[] getExpectedType() {
+                        return ARRAY_TYPE;
                     }
                 };
             }
@@ -158,4 +81,4 @@ public class StringJoinDescriptor extends 
AbstractScalarFunctionDynamicDescripto
     public FunctionIdentifier getIdentifier() {
         return BuiltinFunctions.STRING_JOIN;
     }
-}
+}
\ No newline at end of file

Reply via email to