Added built-in functions: ifmissing(), ifnull(), ifmissingornull()

Change-Id: Id114f6654b9814c5aeca07fffeea04daeb8dca19
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1745
Reviewed-by: Yingyi Bu <buyingyi@gmail.com>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
BAD: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java
index 6f24fc5..7431218 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/util/FunctionCollection.java
@@ -158,6 +158,9 @@
 import org.apache.asterix.runtime.evaluators.functions.GramTokensDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.HashedGramTokensDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.HashedWordTokensDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.IfMissingDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.IfMissingOrNullDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.IfNullDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.InjectFailureDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.IsArrayDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.IsBooleanDescriptor;
@@ -428,6 +431,9 @@
         temp.add(IsUnknownDescriptor.FACTORY);
         temp.add(IsSystemNullDescriptor.FACTORY);
         temp.add(CheckUnknownDescriptor.FACTORY);
+        temp.add(IfMissingDescriptor.FACTORY);
+        temp.add(IfNullDescriptor.FACTORY);
+        temp.add(IfMissingOrNullDescriptor.FACTORY);
 
         // uuid generators (zero independent functions)
         temp.add(CreateUUIDDescriptor.FACTORY);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissing/ifmissing.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissing/ifmissing.1.query.sqlpp
new file mode 100644
index 0000000..2f0d837
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissing/ifmissing.1.query.sqlpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+{
+   "a": isnull(ifmissing()),
+   "b": isnull(ifmissing(missing)),
+   "c": isnull(ifmissing(missing, missing)),
+   "d": ifmissing(missing, missing, true, 1),
+   "e": ifmissing(true, false, 1),
+   "f": isstring(ifmissing("true", true, 1)),
+   "g": if_missing(missing, true, 1),
+   "i": ifmissing(
+            missing,
+            case when get_year(current_datetime()) > 0 then missing else false end,
+            case when get_year(current_datetime()) > 0 then true else null end
+        )
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissingornull/ifmissingornull.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissingornull/ifmissingornull.1.query.sqlpp
new file mode 100644
index 0000000..32f040f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissingornull/ifmissingornull.1.query.sqlpp
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+{
+   "a": isnull(ifmissingornull()),
+   "b": isnull(ifmissingornull(missing)),
+   "c": isnull(ifmissingornull(null)),
+   "d": isnull(ifmissingornull(null, missing)),
+   "e": ifmissingornull(null, true, 1),
+   "f": ifmissingornull(missing, true, 1),
+   "g": ifmissingornull(null, missing, true, 1),
+   "h": if_missing_or_null(null, missing, true, 1),
+   "i": ifmissingornull(
+            missing,
+            null,
+            case when get_year(current_datetime()) > 0 then missing else false end,
+            case when get_year(current_datetime()) > 0 then null else false end,
+            case when get_year(current_datetime()) > 0 then true else missing end
+        )
+};
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifnull/ifnull.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifnull/ifnull.1.query.sqlpp
new file mode 100644
index 0000000..c0683bd
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifnull/ifnull.1.query.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.
+ */
+
+{
+   "a": isnull(null),
+   "b": isnull(ifnull()),
+   "c": isnull(ifnull(null)),
+   "d": ismissing(ifnull(missing)),
+   "e": ifnull(null, true, 1),
+   "f": ifnull(null, null, true, 1),
+   "g": ismissing(ifnull(missing, false, 1)),
+   "h": if_null(null, true),
+   "i": ifnull(
+            null,
+            case when get_year(current_datetime()) > 0 then null else false end,
+            case when get_year(current_datetime()) > 0 then true else missing end
+        )
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissing/ifmissing.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissing/ifmissing.1.adm
new file mode 100644
index 0000000..0a2275f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissing/ifmissing.1.adm
@@ -0,0 +1 @@
+{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "i": true }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissingornull/ifmissingornull.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissingornull/ifmissingornull.1.adm
new file mode 100644
index 0000000..633c503
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissingornull/ifmissingornull.1.adm
@@ -0,0 +1 @@
+{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, "i": true }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifnull/ifnull.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifnull/ifnull.1.adm
new file mode 100644
index 0000000..633c503
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifnull/ifnull.1.adm
@@ -0,0 +1 @@
+{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, "i": true }
\ No newline at end of file
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 d5716d1..29917d4 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -8410,6 +8410,21 @@
         <output-dir compare="Text">promotion_opentype_field_vs_opentype_field_02</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="types">
+      <compilation-unit name="ifmissing">
+        <output-dir compare="Text">ifmissing</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="types">
+      <compilation-unit name="ifnull">
+        <output-dir compare="Text">ifnull</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="types">
+      <compilation-unit name="ifmissingornull">
+        <output-dir compare="Text">ifmissingornull</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
   <test-group name="materialization">
     <test-case FilePath="materialization">
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index e59c379..8897dbf 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -98,6 +98,7 @@
     public static final int INCOMPATIBLE_SEARCH_MODIFIER = 1035;
     public static final int UNKNOWN_SEARCH_MODIFIER = 1036;
     public static final int COMPILATION_BAD_QUERY_PARAMETER_VALUE = 1037;
+    public static final int COMPILATION_ILLEGAL_STATE = 1038;
 
     // Feed errors
     public static final int DATAFLOW_ILLEGAL_STATE = 3001;
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index 487758c..1c93efe 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -84,6 +84,7 @@
 1035 = Incompatible search modifier %1$s for index type %2$s
 1036 = Unknown search modifier type %1$s
 1037 = Invalid query parameter %1$s -- value has to be greater than or equal to %2$s bytes
+1038 = Illegal state. %1$s
 
 # Feed Errors
 3001 = Illegal state.
diff --git a/asterixdb/asterix-doc/pom.xml b/asterixdb/asterix-doc/pom.xml
index fed37b8..9b980e1 100644
--- a/asterixdb/asterix-doc/pom.xml
+++ b/asterixdb/asterix-doc/pom.xml
@@ -56,10 +56,10 @@
                   <filelist dir="${project.basedir}/src/main/markdown/sqlpp" files="0_toc.md,1_intro.md,2_expr_title.md,2_expr.md,3_query_title.md,3_query.md,4_error_title.md,4_error.md,5_ddl.md,appendix_1_title.md,appendix_1_keywords.md,appendix_2_title.md,appendix_2_parameters.md" />
                 </concat>
                 <concat destfile="${project.build.directory}/generated-site/markdown/sqlpp/builtins.md">
-                  <filelist dir="${project.basedir}/src/main/markdown/builtins" files="0_toc.md,1_numeric.md,2_string.md,3_binary.md,4_spatial.md,5_similarity.md,6_tokenizing.md,7_temporal.md,7_allens.md,8_record.md,9_aggregate_sql.md,10_comparison.md,11_type.md,12_misc.md" />
+                  <filelist dir="${project.basedir}/src/main/markdown/builtins" files="0_toc.md,1_numeric.md,2_string.md,3_binary.md,4_spatial.md,5_similarity.md,6_tokenizing.md,7_temporal.md,7_allens.md,8_record.md,9_aggregate_sql.md,10_comparison.md,11_type.md,13_conditional.md,12_misc.md" />
                 </concat>
                 <concat destfile="${project.build.directory}/generated-site/markdown/aql/builtins.md">
-                  <filelist dir="${project.basedir}/src/main/markdown/builtins" files="0_toc.md,1_numeric.md,2_string.md,3_binary.md,4_spatial.md,5_similarity.md,6_tokenizing.md,7_temporal.md,7_allens.md,8_record.md,9_aggregate_aql.md,10_comparison.md,11_type.md,12_misc.md" />
+                  <filelist dir="${project.basedir}/src/main/markdown/builtins" files="0_toc.md,1_numeric.md,2_string.md,3_binary.md,4_spatial.md,5_similarity.md,6_tokenizing.md,7_temporal.md,7_allens.md,8_record.md,9_aggregate_aql.md,10_comparison.md,11_type.md,13_conditional.md,12_misc.md" />
                 </concat>
                 <concat destfile="${project.build.directory}/generated-site/markdown/ansible.md">
                   <filelist dir="${project.basedir}/src/main/installation/" files="ansible_title.md,ansible.md" />
diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/0_toc.md b/asterixdb/asterix-doc/src/main/markdown/builtins/0_toc.md
index 911ee63..e0d23d4 100644
--- a/asterixdb/asterix-doc/src/main/markdown/builtins/0_toc.md
+++ b/asterixdb/asterix-doc/src/main/markdown/builtins/0_toc.md
@@ -32,6 +32,7 @@
 * [Aggregate Functions (Array Functions)](#AggregateFunctions)
 * [Comparison Functions](#ComparisonFunctions)
 * [Type Functions](#TypeFunctions)
+* [Conditional Functions](#ConditionalFunctions)
 * [Miscellaneous Functions](#MiscFunctions)
 
 The system provides various classes of functions to support operations on numeric, string, spatial, and temporal data.
diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/13_conditional.md b/asterixdb/asterix-doc/src/main/markdown/builtins/13_conditional.md
new file mode 100644
index 0000000..04fa943
--- /dev/null
+++ b/asterixdb/asterix-doc/src/main/markdown/builtins/13_conditional.md
@@ -0,0 +1,103 @@
+<!--
+ ! 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.
+ !-->
+
+## <a id="ConditionalFunctions">Conditional Functions</a> <font size="4"><a href="#toc">[Back to TOC]</a></font> ##
+
+### if_null (ifnull) ###
+
+ * Syntax:
+
+        if_null(expression1, expression2, ... expressionN)
+
+ * Finds first argument which value is not `null` and returns that value
+ * Arguments:
+    * `expressionI` : an expression (any type is allowed).
+ * Return Value:
+    * a `null` if all arguments evaluate to `null` or no arguments specified
+    * a value of the first non-`null` argument otherwise
+
+ * Example:
+
+        {
+            "a": if_null(),
+            "b": if_null(null),
+            "c": if_null(null, "asterixdb"),
+            "d": is_missing(if_null(missing))
+        };
+
+ * The expected result is:
+
+        { "a": null, "b": null, "c": "asterixdb", "d": true }
+
+ The function has an alias `ifnull`.
+
+### if_missing (ifmissing) ###
+
+ * Syntax:
+
+        if_missing(expression1, expression2, ... expressionN)
+
+ * Finds first argument which value is not `missing` and returns that value
+ * Arguments:
+    * `expressionI` : an expression (any type is allowed).
+ * Return Value:
+    * a `null` if all arguments evaluate to `missing` or no arguments specified
+    * a value of the first non-`missing` argument otherwise
+
+ * Example:
+
+        {
+            "a": if_missing(),
+            "b": if_missing(missing),
+            "c": if_missing(missing, "asterixdb"),
+            "d": if_missing(null, "asterixdb")
+        };
+
+ * The expected result is:
+
+        { "a": null, "b": null, "c": "asterixdb", "d": null }
+
+ The function has an alias `ifmissing`.
+
+### if_missing_or_null (ifmissingornull) ###
+
+ * Syntax:
+
+        if_missing_or_null(expression1, expression2, ... expressionN)
+
+ * Finds first argument which value is not `null` or `missing` and returns that value
+ * Arguments:
+    * `expressionI` : an expression (any type is allowed).
+ * Return Value:
+    * a `null` if all arguments evaluate to either `null` or `missing`, or no arguments specified
+    * a value of the first non-`null`, non-`missing` argument otherwise
+
+* Example:
+
+        {
+            "a": if_missing_or_null(),
+            "b": if_missing_or_null(null, missing),
+            "c": if_missing_or_null(null, missing, "asterixdb")
+        };
+
+ * The expected result is:
+
+        { "a": null, "b": null, "c": "asterixdb" }
+
+ The function has an alias `ifmissingornull`.
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
index 33e0c52..6a0c05e 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
@@ -62,6 +62,9 @@
         FUNCTION_NAME_MAP.put("isarray", "is-array"); // isarray, internal: is-array
         FUNCTION_NAME_MAP.put("isobject", "is-object"); // isobject, internal: is-object
         FUNCTION_NAME_MAP.put("isobj", "is-object"); // isobj, internal: is-object
+        FUNCTION_NAME_MAP.put("ifmissing", "if-missing"); // ifmissing, internal: if-missing
+        FUNCTION_NAME_MAP.put("ifnull", "if-null"); // ifnull, internal: if-null
+        FUNCTION_NAME_MAP.put("ifmissingornull", "if-missing-or-null"); // ifmissingornull, internal: is-missing-or-null
 
         // Object functions
         FUNCTION_NAME_MAP.put("record-merge", "object-merge"); // record-merge, internal: object-merge
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java
index 415c1ae..561df5e 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TypeResolverUtil.java
@@ -58,6 +58,21 @@
     }
 
     /**
+     * Returns a minimally generalized type that conforms to all input types.
+     *
+     * @param inputTypes,
+     *            a list of input types
+     * @return a generalized type that conforms to all input types.
+     */
+    public static IAType resolve(IAType... inputTypes) {
+        IAType currentType = null;
+        for (IAType type : inputTypes) {
+            currentType = currentType == null ? type : generalizeTypes(currentType, type);
+        }
+        return currentType;
+    }
+
+    /**
      * Decides whether a type cast is needed to covert data instances from the input type to the required type.
      *
      * @param reqType,
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 0769596..6fc3ed9 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
@@ -66,6 +66,9 @@
 import org.apache.asterix.om.typecomputer.impl.FieldAccessNestedResultType;
 import org.apache.asterix.om.typecomputer.impl.FullTextContainsResultTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.GetOverlappingInvervalTypeComputer;
+import org.apache.asterix.om.typecomputer.impl.IfMissingOrNullTypeComputer;
+import org.apache.asterix.om.typecomputer.impl.IfMissingTypeComputer;
+import org.apache.asterix.om.typecomputer.impl.IfNullTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.InjectFailureTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.LocalAvgTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.MinMaxAggTypeComputer;
@@ -792,7 +795,14 @@
     public static final FunctionIdentifier CHECK_UNKNOWN = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
             "check-unknown", 1);
     public static final FunctionIdentifier COLLECTION_TO_SEQUENCE = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
-            "" + "collection-to-sequence", 1);
+            "collection-to-sequence", 1);
+
+    public static final FunctionIdentifier IF_MISSING = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+            "if-missing", FunctionIdentifier.VARARGS);
+    public static final FunctionIdentifier IF_NULL = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+            "if-null", FunctionIdentifier.VARARGS);
+    public static final FunctionIdentifier IF_MISSING_OR_NULL = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+            "if-missing-or-null", FunctionIdentifier.VARARGS);
 
     public static final FunctionIdentifier EXTERNAL_LOOKUP = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
             "external-lookup", FunctionIdentifier.VARARGS);
@@ -880,6 +890,9 @@
         addPrivateFunction(GRAM_TOKENS, OrderedListOfAStringTypeComputer.INSTANCE, true);
         addPrivateFunction(HASHED_GRAM_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE, true);
         addPrivateFunction(HASHED_WORD_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE, true);
+        addFunction(IF_MISSING_OR_NULL, IfMissingOrNullTypeComputer.INSTANCE,  true);
+        addFunction(IF_MISSING, IfMissingTypeComputer.INSTANCE,  true);
+        addFunction(IF_NULL, IfNullTypeComputer.INSTANCE,  true);
         addPrivateFunction(INDEX_SEARCH, AnyTypeComputer.INSTANCE, true);
         addFunction(INT8_CONSTRUCTOR, AInt8TypeComputer.INSTANCE, true);
         addFunction(INT16_CONSTRUCTOR, AInt16TypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AbstractIfMissingOrNullTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AbstractIfMissingOrNullTypeComputer.java
new file mode 100644
index 0000000..2626c6b
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AbstractIfMissingOrNullTypeComputer.java
@@ -0,0 +1,112 @@
+/*
+ * 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.om.typecomputer.impl;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.dataflow.data.common.TypeResolverUtil;
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
+
+abstract class AbstractIfMissingOrNullTypeComputer implements IResultTypeComputer {
+    @Override
+    public IAType computeType(ILogicalExpression expression, IVariableTypeEnvironment env,
+            IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
+        AbstractFunctionCallExpression fce = (AbstractFunctionCallExpression) expression;
+        IAType outPrimeType = null;
+        ATypeTag outQuantifier = null; // could be 'missing' or 'null'
+
+        for (Mutable<ILogicalExpression> argRef : fce.getArguments()) {
+            ILogicalExpression arg = argRef.getValue();
+            IAType argType = (IAType) env.getType(arg);
+            ATypeTag argTypeTag = argType.getTypeTag();
+
+            if (equalsIfType(argTypeTag)) {
+                continue;
+            }
+
+            if (argTypeTag == ATypeTag.UNION) {
+                AUnionType unionType = (AUnionType) argType;
+                if (intersectsIfType(unionType)) {
+                    IAType primeType = getOutputPrimeType(unionType);
+                    outPrimeType = outPrimeType == null ? primeType : TypeResolverUtil.resolve(outPrimeType, primeType);
+                    if (outQuantifier == null) {
+                        outQuantifier = getOutputQuantifier(unionType);
+                    }
+                } else {
+                    // no intersection
+                    if (outPrimeType == null) {
+                        return argType;
+                    } else {
+                        IAType primeType = getOutputPrimeType(unionType);
+                        ATypeTag quantifier = outQuantifier != null ? outQuantifier : getOutputQuantifier(unionType);
+                        return createOutputType(TypeResolverUtil.resolve(outPrimeType, primeType), quantifier);
+                    }
+                }
+            } else {
+                // ANY or no intersection
+                return outPrimeType == null ? argType
+                        : createOutputType(TypeResolverUtil.resolve(outPrimeType, argType), outQuantifier);
+            }
+        }
+
+        if (outPrimeType == null) {
+            return BuiltinType.ANULL;
+        }
+        IAType outType = createOutputType(outPrimeType, ATypeTag.NULL);
+        if (outQuantifier == ATypeTag.MISSING) {
+            outType = createOutputType(outType, ATypeTag.MISSING);
+        }
+        return outType;
+    }
+
+    protected abstract boolean equalsIfType(ATypeTag typeTag);
+
+    protected abstract boolean intersectsIfType(AUnionType type);
+
+    protected abstract ATypeTag getOutputQuantifier(AUnionType type);
+
+    private IAType getOutputPrimeType(AUnionType type) {
+        return type.getActualType();
+    }
+
+    private IAType createOutputType(IAType primeType, ATypeTag quantifier) throws AlgebricksException {
+        if (quantifier == null || primeType.getTypeTag() == ATypeTag.ANY) {
+            return primeType;
+        }
+        switch (quantifier) {
+            case MISSING:
+                return AUnionType.createMissableType(primeType);
+            case NULL:
+                return AUnionType.createNullableType(primeType, null);
+            default:
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, String.valueOf(quantifier));
+        }
+    }
+}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfMissingOrNullTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfMissingOrNullTypeComputer.java
new file mode 100644
index 0000000..fd35c79
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfMissingOrNullTypeComputer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.om.typecomputer.impl;
+
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+
+public class IfMissingOrNullTypeComputer extends AbstractIfMissingOrNullTypeComputer {
+
+    public static final IResultTypeComputer INSTANCE = new IfMissingOrNullTypeComputer();
+
+    @Override
+    protected boolean equalsIfType(ATypeTag typeTag) {
+        return typeTag == ATypeTag.MISSING || typeTag == ATypeTag.NULL;
+    }
+
+    @Override
+    protected boolean intersectsIfType(AUnionType type) {
+        return type.isUnknownableType();
+    }
+
+    @Override
+    protected ATypeTag getOutputQuantifier(AUnionType type) {
+        // 'missing' and 'null' cannot be in the output
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfMissingTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfMissingTypeComputer.java
new file mode 100644
index 0000000..1742921
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfMissingTypeComputer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.om.typecomputer.impl;
+
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+
+public class IfMissingTypeComputer extends AbstractIfMissingOrNullTypeComputer {
+
+    public static final IResultTypeComputer INSTANCE = new IfMissingTypeComputer();
+
+    @Override
+    protected boolean equalsIfType(ATypeTag typeTag) {
+        return typeTag == ATypeTag.MISSING;
+    }
+
+    @Override
+    protected boolean intersectsIfType(AUnionType type) {
+        return type.isMissableType();
+    }
+
+    @Override
+    protected ATypeTag getOutputQuantifier(AUnionType type) {
+        // 'null' may be in the output
+        return type.isNullableType() ? ATypeTag.NULL : null;
+    }
+}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfNullTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfNullTypeComputer.java
new file mode 100644
index 0000000..149b35d
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfNullTypeComputer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.om.typecomputer.impl;
+
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+
+public class IfNullTypeComputer extends IfMissingOrNullTypeComputer {
+
+    public static final IResultTypeComputer INSTANCE = new IfNullTypeComputer();
+
+    @Override
+    protected boolean equalsIfType(ATypeTag typeTag) {
+        return typeTag == ATypeTag.NULL;
+    }
+
+    @Override
+    protected boolean intersectsIfType(AUnionType type) {
+        return type.isNullableType();
+    }
+
+    @Override
+    protected ATypeTag getOutputQuantifier(AUnionType type) {
+        // 'missing' may be in the output
+        return type.isMissableType() ? ATypeTag.MISSING : null;
+    }
+}
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java
index e1d4088..fc4646f 100644
--- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java
@@ -59,8 +59,8 @@
             testTypeComputer(c);
             ++numTypeComputers;
         }
-        // Currently, there are 78 type computers.
-        Assert.assertTrue(numTypeComputers >= 78);
+        // Currently, there are 83 type computers.
+        Assert.assertTrue(numTypeComputers >= 83);
     }
 
     private void testTypeComputer(Class<? extends IResultTypeComputer> c) throws Exception {
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingDescriptor.java
new file mode 100644
index 0000000..984b692
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingDescriptor.java
@@ -0,0 +1,57 @@
+/*
+ * 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 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.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+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;
+
+public class IfMissingDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+    private static final long serialVersionUID = 1L;
+    public static final IFunctionDescriptorFactory FACTORY = IfMissingDescriptor::new;
+
+    @Override
+    public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
+        return new IScalarEvaluatorFactory() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
+                return new IfMissingOrNullDescriptor.AbstractIfEvaluator(ctx, args) {
+                    @Override
+                    protected boolean skip(byte argTypeTag) {
+                        return argTypeTag == ATypeTag.SERIALIZED_MISSING_TYPE_TAG;
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return BuiltinFunctions.IF_MISSING;
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingOrNullDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingOrNullDescriptor.java
new file mode 100644
index 0000000..28db4d6
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingOrNullDescriptor.java
@@ -0,0 +1,96 @@
+/*
+ * 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 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.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+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.dataflow.common.data.accessors.IFrameTupleReference;
+
+public final class IfMissingOrNullDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+    private static final long serialVersionUID = 1L;
+    public static final IFunctionDescriptorFactory FACTORY = IfMissingOrNullDescriptor::new;
+
+    @Override
+    public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) throws AlgebricksException {
+        return new IScalarEvaluatorFactory() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
+                return new AbstractIfEvaluator(ctx, args) {
+                    @Override
+                    protected boolean skip(byte argTypeTag) {
+                        return argTypeTag == ATypeTag.SERIALIZED_MISSING_TYPE_TAG
+                                || argTypeTag == ATypeTag.SERIALIZED_NULL_TYPE_TAG;
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return BuiltinFunctions.IF_MISSING_OR_NULL;
+    }
+
+    public static abstract class AbstractIfEvaluator implements IScalarEvaluator {
+
+        private static final byte[] nullBytes = new byte[] { ATypeTag.SERIALIZED_NULL_TYPE_TAG };
+
+        private final IScalarEvaluator[] argEvals;
+
+        private final IPointable argPtr;
+
+        AbstractIfEvaluator(IHyracksTaskContext ctx, IScalarEvaluatorFactory[] args) throws HyracksDataException {
+            argEvals = new IScalarEvaluator[args.length];
+            for (int i = 0; i < argEvals.length; i++) {
+                argEvals[i] = args[i].createScalarEvaluator(ctx);
+            }
+            argPtr = new VoidPointable();
+        }
+
+        @Override
+        public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException {
+            for (IScalarEvaluator argEval : argEvals) {
+                argEval.evaluate(tuple, argPtr);
+                byte[] bytes = argPtr.getByteArray();
+                int offset = argPtr.getStartOffset();
+                if (!skip(bytes[offset])) {
+                    result.set(argPtr);
+                    return;
+                }
+            }
+            result.set(nullBytes, 0, nullBytes.length);
+        }
+
+        protected abstract boolean skip(byte argTypeTag);
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNullDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNullDescriptor.java
new file mode 100644
index 0000000..11e464a
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNullDescriptor.java
@@ -0,0 +1,58 @@
+/*
+ * 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 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.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+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;
+
+public final class IfNullDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+    private static final long serialVersionUID = 1L;
+    public static final IFunctionDescriptorFactory FACTORY = IfNullDescriptor::new;
+
+    @Override
+    public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) throws AlgebricksException {
+        return new IScalarEvaluatorFactory() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
+                return new IfMissingOrNullDescriptor.AbstractIfEvaluator(ctx, args) {
+                    @Override
+                    protected boolean skip(byte argTypeTag) {
+                        return argTypeTag == ATypeTag.SERIALIZED_NULL_TYPE_TAG;
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return BuiltinFunctions.IF_NULL;
+    }
+}