Fixed issue 152.

git-svn-id: https://asterixdb.googlecode.com/svn/branches/asterix_stabilization@601 eaa15691-b419-025a-1212-ee371bd00084
diff --git a/asterix-app/src/test/resources/runtimets/queries/string/concat1.aql b/asterix-app/src/test/resources/runtimets/queries/string/concat_01.aql
similarity index 81%
rename from asterix-app/src/test/resources/runtimets/queries/string/concat1.aql
rename to asterix-app/src/test/resources/runtimets/queries/string/concat_01.aql
index a6ce63b..2a0b1ab 100644
--- a/asterix-app/src/test/resources/runtimets/queries/string/concat1.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/string/concat_01.aql
@@ -2,7 +2,7 @@
 create dataverse test;
 use dataverse test;
 
-write output to nc1:"rttest/string_concat1.adm";
+write output to nc1:"rttest/string_concat_01.adm";
 
 let $x :=  ["aa", "25991", "bb", "31526"]
 let $c := string-concat($x)
diff --git a/asterix-app/src/test/resources/runtimets/queries/string/concat_02.aql b/asterix-app/src/test/resources/runtimets/queries/string/concat_02.aql
new file mode 100644
index 0000000..c716fcb
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/string/concat_02.aql
@@ -0,0 +1,15 @@
+/*
+ * Description    : Test concat-string function with nulls in the list which is passed as an argument.
+ * Success        : Yes
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+write output to nc1:"rttest/string_concat_02.adm";
+
+let $a := string-concat([null])
+let $b := string-concat([null, "foo"])
+let $c := string-concat(["foo", null])
+return {"a": $a, "b": $b, "c": $c}
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/string/concat1.adm b/asterix-app/src/test/resources/runtimets/results/string/concat_01.adm
similarity index 100%
rename from asterix-app/src/test/resources/runtimets/results/string/concat1.adm
rename to asterix-app/src/test/resources/runtimets/results/string/concat_01.adm
diff --git a/asterix-app/src/test/resources/runtimets/results/string/concat_02.adm b/asterix-app/src/test/resources/runtimets/results/string/concat_02.adm
new file mode 100644
index 0000000..ba4be9f
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/string/concat_02.adm
@@ -0,0 +1 @@
+{ "a": null, "b": null, "c": null }
\ No newline at end of file
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
index e95cb6f..552a093 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
@@ -9,6 +9,7 @@
 import org.apache.commons.lang3.mutable.Mutable;
 
 import edu.uci.ics.asterix.common.functions.FunctionConstants;
+import edu.uci.ics.asterix.dataflow.data.common.AqlNullableTypeComputer;
 import edu.uci.ics.asterix.om.typecomputer.base.IResultTypeComputer;
 import edu.uci.ics.asterix.om.typecomputer.impl.ABooleanTypeComputer;
 import edu.uci.ics.asterix.om.typecomputer.impl.ACircleTypeComputer;
@@ -530,7 +531,7 @@
 
         add(STRING_TO_CODEPOINT, OrderedListOfAInt32TypeComputer.INSTANCE);
         add(CODEPOINT_TO_STRING, AStringTypeComputer.INSTANCE);
-        add(STRING_CONCAT, AStringTypeComputer.INSTANCE);
+        add(STRING_CONCAT, OptionalAStringTypeComputer.INSTANCE);        
         add(SUBSTRING2, Substring2TypeComputer.INSTANCE);
         add(STRING_LENGTH, UnaryStringInt32OrNullTypeComputer.INSTANCE);
         add(STRING_LOWERCASE, UnaryStringOrNullTypeComputer.INSTANCE);
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
index 1c2cee7..2b53f35 100644
--- a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
@@ -1,18 +1,20 @@
 package edu.uci.ics.asterix.runtime.evaluators.functions;
 
+import java.io.DataOutput;
+import java.io.IOException;
+
 import edu.uci.ics.asterix.common.exceptions.AsterixException;
 import edu.uci.ics.asterix.common.functions.FunctionConstants;
-import edu.uci.ics.asterix.dataflow.data.nontagged.serde.AOrderedListSerializerDeserializer;
 import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
 import edu.uci.ics.asterix.om.base.ANull;
 import edu.uci.ics.asterix.om.functions.IFunctionDescriptor;
 import edu.uci.ics.asterix.om.functions.IFunctionDescriptorFactory;
 import edu.uci.ics.asterix.om.types.ATypeTag;
 import edu.uci.ics.asterix.om.types.BuiltinType;
-import edu.uci.ics.asterix.om.types.EnumDeserializer;
 import edu.uci.ics.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
-import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.asterix.runtime.evaluators.common.AsterixListAccessor;
 import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluator;
 import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory;
 import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
@@ -21,8 +23,6 @@
 import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage;
 import edu.uci.ics.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
 import edu.uci.ics.hyracks.dataflow.common.data.util.StringUtils;
-import java.io.DataOutput;
-import java.io.IOException;
 
 /**
  * @author Xiaoyu Ma
@@ -37,24 +37,22 @@
             return new StringConcatDescriptor();
         }
     };
-    private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
-    private final static byte SER_ORDEREDLIST_TYPE_TAG = ATypeTag.ORDEREDLIST.serialize();
-    private final byte stringTypeTag = ATypeTag.STRING.serialize();
-
+    
     @Override
     public ICopyEvaluatorFactory createEvaluatorFactory(final ICopyEvaluatorFactory[] args) {
         return new ICopyEvaluatorFactory() {
-
+            
             private static final long serialVersionUID = 1L;
 
             @Override
             public ICopyEvaluator createEvaluator(final IDataOutputProvider output) throws AlgebricksException {
                 return new ICopyEvaluator() {
 
-                    private DataOutput out = output.getDataOutput();
-                    private ICopyEvaluatorFactory listEvalFactory = args[0];
-                    private ArrayBackedValueStorage outInputList = new ArrayBackedValueStorage();
-                    private ICopyEvaluator evalList = listEvalFactory.createEvaluator(outInputList);
+                    private final AsterixListAccessor listAccessor = new AsterixListAccessor();
+                    private final DataOutput out = output.getDataOutput();
+                    private final ICopyEvaluatorFactory listEvalFactory = args[0];
+                    private final ArrayBackedValueStorage outInputList = new ArrayBackedValueStorage();
+                    private final ICopyEvaluator evalList = listEvalFactory.createEvaluator(outInputList);
                     @SuppressWarnings("unchecked")
                     private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
                             .getSerializerDeserializer(BuiltinType.ANULL);
@@ -64,32 +62,39 @@
                         try {
                             outInputList.reset();
                             evalList.evaluate(tuple);
-                            byte[] serOrderedList = outInputList.getByteArray();
-                            if (serOrderedList[0] == SER_NULL_TYPE_TAG) {
+                            byte[] listBytes = outInputList.getByteArray();
+                            try {
+                                listAccessor.reset(listBytes, 0);
+                            } catch (AsterixException e) {
+                                throw new AlgebricksException(e);
+                            }
+                            if (listAccessor.getItemType() == ATypeTag.NULL) {
                                 nullSerde.serialize(ANull.NULL, out);
                                 return;
                             }
-                            if (serOrderedList[0] != SER_ORDEREDLIST_TYPE_TAG) {
-                                throw new AlgebricksException("Expects String List."
-                                        + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serOrderedList[0]));
-                            }
-                            int size = AOrderedListSerializerDeserializer.getNumberOfItems(serOrderedList);
                             try {
                                 // calculate length first
-                                int utf_8_len = 0;
-                                for (int i = 0; i < size; i++) {
-                                    int itemOffset = AOrderedListSerializerDeserializer
-                                            .getItemOffset(serOrderedList, i);
-                                    utf_8_len += UTF8StringPointable.getUTFLength(serOrderedList, itemOffset);
+                                int utf8Len = 0;
+                                for (int i = 0; i < listAccessor.size(); i++) {
+                                    int itemOffset = listAccessor.getItemOffset(i);
+                                    ATypeTag itemType = listAccessor.getItemType(itemOffset);
+                                    if (itemType != ATypeTag.STRING) {
+                                        if (itemType == ATypeTag.NULL) {
+                                            nullSerde.serialize(ANull.NULL, out);
+                                            return;
+                                        }
+                                        throw new AlgebricksException("Unsupported type " + itemType
+                                                + " in list passed to string-concat function.");
+                                    }
+                                    utf8Len += UTF8StringPointable.getUTFLength(listBytes, itemOffset);
                                 }
-                                out.writeByte(stringTypeTag);
-                                StringUtils.writeUTF8Len(utf_8_len, out);
-                                for (int i = 0; i < size; i++) {
-                                    int itemOffset = AOrderedListSerializerDeserializer
-                                            .getItemOffset(serOrderedList, i);
-                                    utf_8_len = UTF8StringPointable.getUTFLength(serOrderedList, itemOffset);
-                                    for (int j = 0; j < utf_8_len; j++) {
-                                        out.writeByte(serOrderedList[2 + itemOffset + j]);
+                                out.writeByte(ATypeTag.STRING.serialize());
+                                StringUtils.writeUTF8Len(utf8Len, out);
+                                for (int i = 0; i < listAccessor.size(); i++) {
+                                    int itemOffset = listAccessor.getItemOffset(i);
+                                    utf8Len = UTF8StringPointable.getUTFLength(listBytes, itemOffset);
+                                    for (int j = 0; j < utf8Len; j++) {
+                                        out.writeByte(listBytes[2 + itemOffset + j]);
                                     }
                                 }
                             } catch (AsterixException ex) {