Repository: asterixdb
Updated Branches:
  refs/heads/master dba817c9b -> 5cdaa5d6a


http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
index 4e1ab1f..b945a40 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
@@ -47,7 +47,7 @@ public class GenerateColumnNameVisitor extends 
AbstractSqlppExpressionScopingVis
 
     @Override
     public Expression visit(Projection projection, ILangExpression arg) throws 
CompilationException {
-        if (!projection.star() && projection.getName() == null) {
+        if (!projection.star() && !projection.varStar() && 
projection.getName() == null) {
             
projection.setName(SqlppVariableUtil.variableNameToDisplayedFieldName(context.newVariable().getValue()));
         }
         return super.visit(projection, arg);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
index 270b366..fa049fe 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
@@ -130,9 +130,12 @@ public class InlineColumnAliasVisitor extends 
AbstractSqlppExpressionScopingVisi
     private Map<Expression, Expression> mapProjections(List<Projection> 
projections) {
         Map<Expression, Expression> exprMap = new HashMap<>();
         for (Projection projection : projections) {
-            exprMap.put(
-                    new VariableExpr(new 
VarIdentifier(SqlppVariableUtil.toInternalVariableName(projection.getName()))),
-                    projection.getExpression());
+            if (!projection.star() && !projection.varStar()) {
+                exprMap.put(
+                        new VariableExpr(
+                                new 
VarIdentifier(SqlppVariableUtil.toInternalVariableName(projection.getName()))),
+                        projection.getExpression());
+            }
         }
         return exprMap;
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
index d1d95ac..db5b780 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
@@ -118,7 +118,7 @@ public class SqlppInlineUdfsVisitor extends 
AbstractInlineUdfsVisitor
 
     @Override
     public Boolean visit(Projection projection, List<FunctionDecl> funcs) 
throws CompilationException {
-        if (projection.star() == true) {
+        if (projection.star()) {
             return false;
         }
         Pair<Boolean, Expression> p = 
inlineUdfsInExpr(projection.getExpression(), funcs);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index ffd63d4..1166148 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -131,7 +131,7 @@ public class DeepCopyVisitor extends 
AbstractSqlppQueryExpressionVisitor<ILangEx
     @Override
     public Projection visit(Projection projection, Void arg) throws 
CompilationException {
         return new Projection(projection.star() ? null : (Expression) 
projection.getExpression().accept(this, arg),
-                projection.getName(), projection.star(), 
projection.exprStar());
+                projection.getName(), projection.star(), projection.varStar());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
index 09eaa59..5f6b75b 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
@@ -127,7 +127,7 @@ public class SqlppAstPrintVisitor extends QueryPrintVisitor 
implements ISqlppVis
             out.println(skip(step) + "*");
         } else {
             projection.getExpression().accept(this, step);
-            out.println(skip(step) + projection.getName());
+            out.println(skip(step) + (projection.varStar() ? ".*" : 
projection.getName()));
         }
         return null;
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
index 65e9255..0222e0e 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
@@ -197,7 +197,7 @@ public class SqlppCloneAndSubstituteVariablesVisitor 
extends CloneAndSubstituteV
             return new Pair<>(projection, env);
         }
         Projection newProjection = new Projection((Expression) 
projection.getExpression().accept(this, env).first,
-                projection.getName(), projection.star(), 
projection.exprStar());
+                projection.getName(), projection.star(), projection.varStar());
         return new Pair<>(newProjection, env);
     }
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
index 51444e8..755cc69 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
@@ -126,9 +126,13 @@ public class SqlppFormatPrintVisitor extends 
FormatPrintVisitor implements ISqlp
             return null;
         }
         projection.getExpression().accept(this, step);
-        String name = projection.getName();
-        if (name != null) {
-            out.print(" as " + name);
+        if (projection.varStar()) {
+            out.print(".* ");
+        } else {
+            String name = projection.getName();
+            if (name != null) {
+                out.print(" as " + name);
+            }
         }
         return null;
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj 
b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index a532a6b..7a99814 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -2477,15 +2477,12 @@ RecordConstructor RecordConstructor() throws 
ParseException:
 
 FieldBinding FieldBinding() throws ParseException:
 {
-    FieldBinding fb = new FieldBinding();
     Expression left, right;
 }
 {
     left = Expression() <COLON> right = Expression()
     {
-      fb.setLeftExpr(left);
-      fb.setRightExpr(right);
-      return fb;
+      return new FieldBinding(left, right);
     }
 }
 
@@ -2768,23 +2765,24 @@ Projection Projection() throws ParseException :
   Identifier identifier = null;
   String name = null;
   boolean star = false;
-  boolean exprStar = false;
+  boolean varStar = false;
 }
 {
   (
-    LOOKAHEAD(2)
-    expr = Expression() ((<AS>)? name = Identifier())?
-    | expr = Expression() <DOT> <MUL> {exprStar = true; }
-    | <MUL> {star = true; }
+    <MUL> {star = true; }
+    | LOOKAHEAD(3) expr = VariableRef() <DOT> <MUL> {varStar = true; }
+    | expr = Expression() ((<AS>)? name = Identifier())?
+      {
+        if (name == null) {
+          String generatedColumnIdentifier = 
ExpressionToVariableUtil.getGeneratedIdentifier(expr, false);
+          if (generatedColumnIdentifier != null) {
+            name = 
SqlppVariableUtil.toUserDefinedName(generatedColumnIdentifier);
+          }
+        }
+      }
   )
   {
-    if(!star && name == null){
-       String generatedColumnIdentifier = 
ExpressionToVariableUtil.getGeneratedIdentifier(expr, false);
-       if(generatedColumnIdentifier != null){
-            name = 
SqlppVariableUtil.toUserDefinedName(generatedColumnIdentifier);
-       }
-    }
-    return new Projection(expr, name, star, exprStar);
+    return new Projection(expr, name, star, varStar);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
 
b/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
index 975b4f4..10863cd 100644
--- 
a/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
+++ 
b/asterixdb/asterix-om/src/main/java/org/apache/asterix/builders/RecordBuilder.java
@@ -310,4 +310,11 @@ public class RecordBuilder implements IARecordBuilder {
         return -1;
     }
 
+    public IBinaryHashFunction getFieldNameHashFunction() {
+        return utf8HashFunction;
+    }
+
+    public IBinaryComparator getFieldNameComparator() {
+        return utf8Comparator;
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
 
b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index d749899..d920286 100644
--- 
a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ 
b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -178,6 +178,10 @@ public class BuiltinFunctions {
     // objects
     public static final FunctionIdentifier RECORD_MERGE =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, 
"object-merge", 2);
+    public static final FunctionIdentifier RECORD_CONCAT =
+            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, 
"object-concat", FunctionIdentifier.VARARGS);
+    public static final FunctionIdentifier RECORD_CONCAT_STRICT =
+            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, 
"object-concat-strict", FunctionIdentifier.VARARGS);
     public static final FunctionIdentifier REMOVE_FIELDS =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, 
"object-remove-fields", 2);
     public static final FunctionIdentifier ADD_FIELDS =
@@ -1174,6 +1178,8 @@ public class BuiltinFunctions {
 
         // objects
         addFunction(RECORD_MERGE, RecordMergeTypeComputer.INSTANCE, true);
+        addFunction(RECORD_CONCAT, OpenARecordTypeComputer.INSTANCE, true);
+        addPrivateFunction(RECORD_CONCAT_STRICT, 
OpenARecordTypeComputer.INSTANCE, true);
         addFunction(ADD_FIELDS, RecordAddFieldsTypeComputer.INSTANCE, true);
         addFunction(REMOVE_FIELDS, RecordRemoveFieldsTypeComputer.INSTANCE, 
true);
         addPrivateFunction(CLOSED_RECORD_CONSTRUCTOR, 
ClosedRecordConstructorResultType.INSTANCE, true);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatDescriptor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatDescriptor.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatDescriptor.java
new file mode 100644
index 0000000..0a3ba36
--- /dev/null
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatDescriptor.java
@@ -0,0 +1,67 @@
+/*
+ * 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.records;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptor;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.functions.IFunctionTypeInferer;
+import org.apache.asterix.om.types.ARecordType;
+import 
org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.asterix.runtime.functions.FunctionTypeInferers;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+
+/**
+ * Concatenates multiple records into one. Returns {@code null} if an argument 
is not a record.
+ */
+public class RecordConcatDescriptor extends 
AbstractScalarFunctionDynamicDescriptor {
+
+    public static final IFunctionDescriptorFactory FACTORY = new 
IFunctionDescriptorFactory() {
+        @Override
+        public IFunctionDescriptor createFunctionDescriptor() {
+            return new RecordConcatDescriptor();
+        }
+
+        @Override
+        public IFunctionTypeInferer createFunctionTypeInferer() {
+            return new FunctionTypeInferers.RecordConcatTypeInferer();
+        }
+    };
+
+    private static final long serialVersionUID = 1L;
+
+    private ARecordType[] argTypes;
+
+    @Override
+    public void setImmutableStates(Object... states) {
+        argTypes = (ARecordType[]) states;
+    }
+
+    @Override
+    public IScalarEvaluatorFactory createEvaluatorFactory(final 
IScalarEvaluatorFactory[] args) {
+        return new RecordConcatEvalFactory(args, argTypes, false);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return BuiltinFunctions.RECORD_CONCAT;
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatEvalFactory.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatEvalFactory.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatEvalFactory.java
new file mode 100644
index 0000000..71f035e
--- /dev/null
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatEvalFactory.java
@@ -0,0 +1,245 @@
+/*
+ * 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.records;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.List;
+
+import org.apache.asterix.builders.RecordBuilder;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.pointables.ARecordVisitablePointable;
+import org.apache.asterix.om.pointables.base.DefaultOpenFieldType;
+import org.apache.asterix.om.pointables.base.IVisitablePointable;
+import org.apache.asterix.om.pointables.cast.ACastVisitor;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.runtime.evaluators.functions.BinaryHashMap;
+import org.apache.asterix.runtime.exceptions.TypeMismatchException;
+import org.apache.hyracks.algebricks.common.utils.Triple;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+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.data.std.util.BinaryEntry;
+import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+
+class RecordConcatEvalFactory implements IScalarEvaluatorFactory {
+
+    private static final long serialVersionUID = 1L;
+
+    private final IScalarEvaluatorFactory[] args;
+
+    private final ARecordType[] argTypes;
+
+    private final boolean failOnArgTypeMismatch;
+
+    RecordConcatEvalFactory(IScalarEvaluatorFactory[] args, ARecordType[] 
argTypes, boolean failOnArgTypeMismatch) {
+        this.args = args;
+        this.argTypes = argTypes;
+        this.failOnArgTypeMismatch = failOnArgTypeMismatch;
+    }
+
+    @Override
+    public IScalarEvaluator createScalarEvaluator(IHyracksTaskContext ctx) 
throws HyracksDataException {
+        IScalarEvaluator[] argEvals = new IScalarEvaluator[args.length];
+        for (int i = 0; i < args.length; i++) {
+            argEvals[i] = args[i].createScalarEvaluator(ctx);
+        }
+        return new RecordConcatEvaluator(argEvals);
+    }
+
+    private final class RecordConcatEvaluator implements IScalarEvaluator {
+
+        private static final int TABLE_FRAME_SIZE = 32768;
+        private static final int TABLE_SIZE = 100;
+
+        private final IScalarEvaluator[] argEvals;
+        private final IPointable[] argPointables;
+        private final ARecordVisitablePointable[] argRecordPointables;
+        private final ARecordVisitablePointable openRecordPointable;
+
+        private final BitSet castRequired;
+        private ACastVisitor castVisitor;
+        private Triple<IVisitablePointable, IAType, Boolean> castVisitorArg;
+
+        private final RecordBuilder outRecordBuilder;
+        private final ArrayBackedValueStorage resultStorage;
+        private final DataOutput resultOutput;
+
+        private final BinaryHashMap fieldMap;
+        private final BinaryEntry keyEntry;
+        private final BinaryEntry valEntry;
+
+        private RecordConcatEvaluator(IScalarEvaluator[] argEvals) {
+            this.argEvals = argEvals;
+
+            argPointables = new IPointable[args.length];
+            argRecordPointables = new ARecordVisitablePointable[args.length];
+            openRecordPointable = new 
ARecordVisitablePointable(DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE);
+
+            resultStorage = new ArrayBackedValueStorage();
+            resultOutput = resultStorage.getDataOutput();
+            outRecordBuilder = new RecordBuilder();
+            outRecordBuilder.reset(openRecordPointable.getInputRecordType());
+
+            fieldMap = new BinaryHashMap(TABLE_SIZE, TABLE_FRAME_SIZE, 
outRecordBuilder.getFieldNameHashFunction(),
+                    outRecordBuilder.getFieldNameHashFunction(), 
outRecordBuilder.getFieldNameComparator());
+            keyEntry = new BinaryEntry();
+            valEntry = new BinaryEntry();
+            valEntry.set(new byte[0], 0, 0);
+
+            castRequired = new BitSet();
+            for (int i = 0; i < args.length; i++) {
+                argPointables[i] = new VoidPointable();
+                ARecordType argType = argTypes[i];
+                if (argType != null) {
+                    argRecordPointables[i] = new 
ARecordVisitablePointable(argType);
+                    if (hasDerivedType(argType.getFieldTypes())) {
+                        castRequired.set(i);
+                        if (castVisitor == null) {
+                            castVisitor = new ACastVisitor();
+                            castVisitorArg = new Triple<>(openRecordPointable, 
openRecordPointable.getInputRecordType(),
+                                    Boolean.FALSE);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void evaluate(IFrameTupleReference tuple, IPointable result) 
throws HyracksDataException {
+            resultStorage.reset();
+            if (validateArgs(tuple)) {
+                processArgs();
+            }
+            result.set(resultStorage);
+        }
+
+        private boolean validateArgs(IFrameTupleReference tuple) throws 
HyracksDataException {
+            if (args.length == 0) {
+                writeTypeTag(ATypeTag.SERIALIZED_NULL_TYPE_TAG);
+                return false;
+            }
+            boolean returnMissing = false, returnNull = false;
+            for (int i = 0; i < argEvals.length; i++) {
+                IPointable argPtr = argPointables[i];
+                argEvals[i].evaluate(tuple, argPtr);
+
+                byte[] data = argPtr.getByteArray();
+                int offset = argPtr.getStartOffset();
+                byte typeTag = data[offset];
+
+                if (typeTag == ATypeTag.SERIALIZED_MISSING_TYPE_TAG) {
+                    returnMissing = true;
+                    if (!failOnArgTypeMismatch) {
+                        break;
+                    }
+                } else if (typeTag == ATypeTag.SERIALIZED_NULL_TYPE_TAG) {
+                    returnNull = true;
+                } else if (typeTag != ATypeTag.SERIALIZED_RECORD_TYPE_TAG) {
+                    if (failOnArgTypeMismatch) {
+                        throw new 
TypeMismatchException(BuiltinFunctions.RECORD_CONCAT, i, typeTag,
+                                ATypeTag.SERIALIZED_RECORD_TYPE_TAG);
+                    } else {
+                        returnNull = true;
+                    }
+                }
+            }
+            if (returnMissing) {
+                writeTypeTag(ATypeTag.SERIALIZED_MISSING_TYPE_TAG);
+                return false;
+            }
+            if (returnNull) {
+                writeTypeTag(ATypeTag.SERIALIZED_NULL_TYPE_TAG);
+                return false;
+            }
+            return true;
+        }
+
+        private void processArgs() throws HyracksDataException {
+            outRecordBuilder.init();
+            fieldMap.clear();
+            for (int i = argEvals.length - 1; i >= 0; i--) {
+                try {
+                    appendRecord(argPointables[i], argRecordPointables[i], 
castRequired.get(i));
+                } catch (IOException e) {
+                    throw new HyracksDataException(e);
+                }
+            }
+            outRecordBuilder.write(resultOutput, true);
+        }
+
+        private void appendRecord(IPointable recordPtr, 
ARecordVisitablePointable argVisitablePointable,
+                boolean argCastRequired) throws IOException {
+
+            ARecordVisitablePointable recordPointable;
+            if (argVisitablePointable != null) {
+                argVisitablePointable.set(recordPtr);
+                if (argCastRequired) {
+                    argVisitablePointable.accept(castVisitor, castVisitorArg);
+                    recordPointable = openRecordPointable;
+                } else {
+                    recordPointable = argVisitablePointable;
+                }
+            } else {
+                openRecordPointable.set(recordPtr);
+                recordPointable = openRecordPointable;
+            }
+
+            List<IVisitablePointable> fieldNames = 
recordPointable.getFieldNames();
+            List<IVisitablePointable> fieldValues = 
recordPointable.getFieldValues();
+            for (int i = 0, fieldCount = fieldNames.size(); i < fieldCount; 
i++) {
+                IVisitablePointable fieldName = fieldNames.get(i);
+                if (canAppendField(fieldName.getByteArray(), 
fieldName.getStartOffset() + 1,
+                        fieldName.getLength() - 1)) {
+                    outRecordBuilder.addField(fieldName, fieldValues.get(i));
+                }
+            }
+        }
+
+        private boolean canAppendField(byte[] buf, int offset, int length) 
throws HyracksDataException {
+            keyEntry.set(buf, offset, length);
+            return fieldMap.put(keyEntry, valEntry) == null;
+        }
+
+        private boolean hasDerivedType(IAType[] types) {
+            for (IAType type : types) {
+                if (type.getTypeTag().isDerivedType()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void writeTypeTag(byte typeTag) throws HyracksDataException {
+            try {
+                resultOutput.writeByte(typeTag);
+            } catch (IOException e) {
+                throw new HyracksDataException(e);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatStrictDescriptor.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatStrictDescriptor.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatStrictDescriptor.java
new file mode 100644
index 0000000..77fe301
--- /dev/null
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordConcatStrictDescriptor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.records;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptor;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.functions.IFunctionTypeInferer;
+import org.apache.asterix.om.types.ARecordType;
+import 
org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.asterix.runtime.functions.FunctionTypeInferers;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+
+/**
+ * Concatenates multiple records into one. Fails if an argument is not a 
record.
+ */
+public class RecordConcatStrictDescriptor extends 
AbstractScalarFunctionDynamicDescriptor {
+    public static final IFunctionDescriptorFactory FACTORY = new 
IFunctionDescriptorFactory() {
+        @Override
+        public IFunctionDescriptor createFunctionDescriptor() {
+            return new RecordConcatStrictDescriptor();
+        }
+
+        @Override
+        public IFunctionTypeInferer createFunctionTypeInferer() {
+            return new FunctionTypeInferers.RecordConcatTypeInferer();
+        }
+    };
+
+    private static final long serialVersionUID = 1L;
+
+    private ARecordType[] argTypes;
+
+    @Override
+    public void setImmutableStates(Object... states) {
+        argTypes = (ARecordType[]) states;
+    }
+
+    @Override
+    public IScalarEvaluatorFactory createEvaluatorFactory(final 
IScalarEvaluatorFactory[] args) {
+        return new RecordConcatEvalFactory(args, argTypes, true);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return BuiltinFunctions.RECORD_CONCAT_STRICT;
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
index f3eb6c9..a111642 100644
--- 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
@@ -270,6 +270,8 @@ import 
org.apache.asterix.runtime.evaluators.functions.records.FieldAccessNested
 import 
org.apache.asterix.runtime.evaluators.functions.records.GetRecordFieldValueDescriptor;
 import 
org.apache.asterix.runtime.evaluators.functions.records.GetRecordFieldsDescriptor;
 import 
org.apache.asterix.runtime.evaluators.functions.records.RecordAddFieldsDescriptor;
+import 
org.apache.asterix.runtime.evaluators.functions.records.RecordConcatDescriptor;
+import 
org.apache.asterix.runtime.evaluators.functions.records.RecordConcatStrictDescriptor;
 import 
org.apache.asterix.runtime.evaluators.functions.records.RecordMergeDescriptor;
 import 
org.apache.asterix.runtime.evaluators.functions.records.RecordPairsDescriptor;
 import 
org.apache.asterix.runtime.evaluators.functions.records.RecordRemoveFieldsDescriptor;
@@ -424,9 +426,11 @@ public final class FunctionCollection {
         fc.add(AndDescriptor.FACTORY);
         fc.add(OrDescriptor.FACTORY);
 
-        // Record constructors
+        // Record constructors / functions
         fc.add(ClosedRecordConstructorDescriptor.FACTORY);
         fc.add(OpenRecordConstructorDescriptor.FACTORY);
+        fc.add(RecordConcatDescriptor.FACTORY);
+        fc.add(RecordConcatStrictDescriptor.FACTORY);
 
         // List constructors
         fc.add(OrderedListConstructorDescriptor.FACTORY);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java
 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java
index b8d9778..6261fb3 100644
--- 
a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java
+++ 
b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java
@@ -27,6 +27,7 @@ import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionTypeInferer;
 import org.apache.asterix.om.pointables.base.DefaultOpenFieldType;
 import org.apache.asterix.om.typecomputer.base.TypeCastUtils;
+import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.AUnionType;
@@ -277,4 +278,23 @@ public final class FunctionTypeInferers {
             fd.setImmutableStates(outType, type0, type1);
         }
     }
+
+    public static final class RecordConcatTypeInferer implements 
IFunctionTypeInferer {
+        @Override
+        public void infer(ILogicalExpression expr, IFunctionDescriptor fd, 
IVariableTypeEnvironment context,
+                CompilerProperties compilerProps) throws AlgebricksException {
+            AbstractFunctionCallExpression f = 
(AbstractFunctionCallExpression) expr;
+            List<Mutable<ILogicalExpression>> args = f.getArguments();
+            int n = args.size();
+            ARecordType[] argRecordTypes = new ARecordType[n];
+            for (int i = 0; i < n; i++) {
+                IAType argType = (IAType) 
context.getType(args.get(i).getValue());
+                IAType t = TypeComputeUtils.getActualType(argType);
+                if (t.getTypeTag() == ATypeTag.OBJECT) {
+                    argRecordTypes[i] = (ARecordType) t;
+                }
+            }
+            fd.setImmutableStates((Object[]) argRecordTypes);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5cdaa5d6/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/string/UTF8StringUtil.java
----------------------------------------------------------------------
diff --git 
a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/string/UTF8StringUtil.java
 
b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/string/UTF8StringUtil.java
index 5d69448..11fb6c0 100644
--- 
a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/string/UTF8StringUtil.java
+++ 
b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/string/UTF8StringUtil.java
@@ -35,7 +35,7 @@ import 
org.apache.hyracks.util.encoding.VarLenIntEncoderDecoder;
 public class UTF8StringUtil {
     public static char charAt(byte[] b, int s) {
         if (s >= b.length) {
-            throw new ArrayIndexOutOfBoundsException("Are you crazy?");
+            throw new ArrayIndexOutOfBoundsException(s);
         }
         int c = b[s] & 0xff;
         switch (c >> 4) {

Reply via email to