You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by dl...@apache.org on 2020/08/27 20:50:09 UTC

[asterixdb] branch master updated: [NO ISSUE][COMP] CREATE OR REPLACE FUNCTION

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

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


The following commit(s) were added to refs/heads/master by this push:
     new dcb4ad2  [NO ISSUE][COMP] CREATE OR REPLACE FUNCTION
dcb4ad2 is described below

commit dcb4ad250d1625881f3d346912ceb137963e1148
Author: Dmitry Lychagin <dm...@couchbase.com>
AuthorDate: Wed Aug 26 15:49:09 2020 -0700

    [NO ISSUE][COMP] CREATE OR REPLACE FUNCTION
    
    - user model changes: yes
    - storage format changes: no
    - interface changes: no
    
    Details:
    - Support CREATE OR REPLACE FUNCTION
    - Support parameter list in DROP FUNCTION
    - Replace "any" type in function metadata with null
    - Refactor builtin function registry
    - Align inline type handling in
      CREATE/DROP FUNCTION/DATASET
    
    Change-Id: Ie603fa9c228a4d1b0fc0ed8275a9b2188e7007d9
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/7543
    Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Dmitry Lychagin <dm...@couchbase.com>
    Reviewed-by: Ian Maxon <im...@uci.edu>
---
 .../optimizer/base/AsterixOptimizationContext.java |   9 +-
 .../rules/DisjunctivePredicateToJoinRule.java      |   4 +-
 .../asterix/optimizer/rules/FuzzyJoinRule.java     |  53 ++++
 ...duceDynamicTypeCastForExternalFunctionRule.java |   7 +-
 .../rules/RewriteDistinctAggregateRule.java        |   2 +-
 .../rules/SetAsterixMemoryRequirementsRule.java    |  19 +-
 .../optimizer/rules/UnnestToDataScanRule.java      |   8 +-
 .../translator/LangExpressionToPlanTranslator.java |  50 ++--
 .../SqlppExpressionToPlanTranslator.java           |   6 +-
 .../app/function/ActiveRequestsDatasource.java     |   2 +-
 .../app/function/CompletedRequestsDatasource.java  |   2 +-
 .../app/function/DatasetResourcesDatasource.java   |   2 +-
 .../asterix/app/function/DumpIndexDatasource.java  |   2 +-
 .../app/function/JobSummariesDatasource.java       |   2 +-
 .../asterix/app/function/PingDatasource.java       |   2 +-
 .../app/function/StorageComponentsDatasource.java  |   2 +-
 .../TPCDSAllTablesDataGeneratorDatasource.java     |   6 +-
 .../TPCDSSingleTableDataGeneratorDatasource.java   |   6 +-
 .../asterix/app/translator/QueryTranslator.java    | 293 +++++++++++++--------
 .../asterix/test/runtime/NullMissingTest.java      |  38 +--
 .../metadata/queries/basic/meta13/meta13.1.ddl.aql |   2 +-
 .../metadata/results/basic/meta06/meta06.1.adm     |   2 +-
 .../create-or-replace-function-1.1.ddl.sqlpp}      |   4 +-
 .../create-or-replace-function-1.10.query.sqlpp}   |  28 +-
 .../create-or-replace-function-1.11.ddl.sqlpp}     |  10 +-
 .../create-or-replace-function-1.12.query.sqlpp}   |  28 +-
 .../create-or-replace-function-1.2.lib.sqlpp}      |   3 +-
 .../create-or-replace-function-1.3.ddl.sqlpp}      |   8 +-
 .../create-or-replace-function-1.4.query.sqlpp}    |  28 +-
 .../create-or-replace-function-1.5.ddl.sqlpp}      |  12 +-
 .../create-or-replace-function-1.6.query.sqlpp}    |  28 +-
 .../create-or-replace-function-1.7.ddl.sqlpp}      |  13 +-
 .../create-or-replace-function-1.8.query.sqlpp}    |  28 +-
 .../create-or-replace-function-1.9.ddl.sqlpp}      |  10 +-
 .../mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp    |   2 +-
 ...rop-function-no-longer-used-by-feed.3.ddl.sqlpp |   3 +-
 .../drop-function-used-by-feed.3.ddl.sqlpp         |   2 +-
 .../drop_if_exists/drop_if_exists.2.ddl.sqlpp      |   2 +-
 .../drop_if_exists/drop_if_exists.3.ddl.sqlpp      |   2 +-
 .../types/default/default.3.ddl.sqlpp              |   2 +-
 .../create-or-replace-function-1.1.ddl.sqlpp}      |  21 +-
 .../create-or-replace-function-1.2.query.sqlpp}    |   7 +-
 .../create-or-replace-function-1.3.query.sqlpp}    |   6 +-
 .../drop-dependency-3/drop-dependency.3.ddl.sqlpp  |   2 +-
 .../drop-dependency-5/drop-dependency.5.ddl.sqlpp  |   2 +-
 .../drop-function-1/drop-function-1.1.ddl.sqlpp}   |  12 +-
 .../drop-function-1/drop-function-1.2.query.sqlpp} |   3 +-
 .../drop-function-1/drop-function-1.3.ddl.sqlpp}   |   2 +-
 .../drop-function-1/drop-function-1.4.query.sqlpp} |   8 +-
 .../query-issue455/query-issue455.4.ddl.sqlpp      |   2 +-
 .../udf33_overloading.1.ddl.sqlpp}                 |  16 +-
 .../udf33_overloading.2.query.sqlpp}               |  11 +-
 .../cross-dataverse/cross-dv15/cross-dv15.1.adm    |   6 +-
 .../create-or-replace-function-1.10.adm            |   3 +
 .../create-or-replace-function-1.12.adm            |   3 +
 .../create-or-replace-function-1.4.adm             |   3 +
 .../create-or-replace-function-1.6.adm             |   3 +
 .../create-or-replace-function-1.8.adm             |   3 +
 .../udf_metadata/udf_metadata.3.adm                |   4 +-
 .../create-or-replace-function-1.2.adm             |   1 +
 .../create-or-replace-function-1.3.adm             |   2 +
 .../drop-function-1/drop-function-1.2.adm          |   1 +
 .../single-line-definition.1.adm                   |   2 +-
 .../user-defined-functions/udf28/udf28.1.adm       |   2 +-
 .../udf32_metadata/udf32_metadata.2.adm            |   6 +-
 .../udf33_overloading/udf33_overloading.2.adm      |   1 +
 .../closed-record-constructor_01.3.ast             |   2 +-
 .../closed-record-constructor_02.3.ast             |   6 +-
 .../open-record-constructor_01.3.ast               |   2 +-
 .../open-record-constructor_02.3.ast               |   6 +-
 .../window/misc_01/misc_01.3.ast                   |   4 +-
 .../resources/runtimets/testsuite_it_sqlpp.xml     |   7 +-
 .../test/resources/runtimets/testsuite_sqlpp.xml   |  32 ++-
 .../common/functions/FunctionSignature.java        |  34 ++-
 .../main/markdown/sqlpp/5_ddl_function_removal.md  |  14 +-
 .../visitor/AqlFunctionCallResolverVisitor.java    |   9 +-
 .../base/AbstractAqlSimpleExpressionVisitor.java   |  12 +
 asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj  |   2 +-
 .../common/statement/CreateFunctionStatement.java  |  18 +-
 .../common/statement/FunctionDropStatement.java    |   2 +-
 .../asterix/lang/common/util/FunctionUtil.java     |  50 ++--
 .../lang/common/visitor/FormatPrintVisitor.java    |   6 +-
 .../lang/common/visitor/QueryPrintVisitor.java     |   2 +-
 .../lang/sqlpp/rewrites/SqlppQueryRewriter.java    |  13 +-
 .../CheckNonFunctionalExpressionVisitor.java       |  25 +-
 .../lang/sqlpp/visitor/SqlppAstPrintVisitor.java   |   4 +-
 .../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj    |  86 ++++--
 .../extension/grammar/GrammarExtensionMojo.java    |  16 +-
 .../apache/asterix/metadata/MetadataManager.java   |  26 +-
 .../org/apache/asterix/metadata/MetadataNode.java  | 187 ++++++-------
 .../asterix/metadata/api/IMetadataManager.java     |  18 ++
 .../apache/asterix/metadata/api/IMetadataNode.java |  22 ++
 .../metadata/declared/FunctionDataSource.java      |  10 +-
 .../metadata/declared/MetadataProvider.java        |   2 +-
 .../apache/asterix/metadata/entities/Dataset.java  |   9 +
 .../apache/asterix/metadata/entities/Function.java |  15 +-
 .../AbstractTupleTranslator.java                   |   4 +
 .../FunctionTupleTranslator.java                   |  98 ++++---
 .../functions/ExternalFunctionCompilerUtil.java    |  54 +++-
 .../apache/asterix/metadata/utils/TypeUtil.java    |  48 +++-
 .../data/common/ExpressionTypeComputer.java        |  18 +-
 .../common/PartialAggregationTypeComputer.java     |   4 +-
 .../asterix/om/functions/BuiltinFunctionInfo.java} |  29 +-
 .../asterix/om/functions/BuiltinFunctions.java     | 257 ++++++++----------
 .../asterix/om/functions/ExternalFunctionInfo.java |  10 +-
 .../apache/asterix/om/functions/FunctionInfo.java  |  39 +--
 .../functions/IFunctionToDataSourceRewriter.java   |   2 -
 .../test/om/typecomputer/ExceptionTest.java        |  59 ++---
 .../runtime/formats/NonTaggedDataFormat.java       |   4 +-
 .../AbstractFunctionCallExpression.java            |  18 +-
 .../core/algebra/functions/FunctionIdentifier.java |  27 +-
 111 files changed, 1292 insertions(+), 884 deletions(-)

diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java
index 20673eb..51c03eb 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java
@@ -23,7 +23,6 @@ import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.asterix.metadata.declared.DataSource;
-import org.apache.asterix.metadata.declared.DataSourceId;
 import org.apache.hyracks.algebricks.common.constraints.AlgebricksPartitionConstraint;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IConflictingTypeResolver;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionEvalSizeComputer;
@@ -40,7 +39,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 
 public final class AsterixOptimizationContext extends AlgebricksOptimizationContext {
 
-    private final Int2ObjectMap<Set<DataSourceId>> dataSourceMap = new Int2ObjectOpenHashMap<>();
+    private final Int2ObjectMap<Set<DataSource>> dataSourceMap = new Int2ObjectOpenHashMap<>();
 
     public AsterixOptimizationContext(int varCounter, IExpressionEvalSizeComputer expressionEvalSizeComputer,
             IMergeAggregationExpressionFactory mergeAggregationExpressionFactory,
@@ -55,15 +54,15 @@ public final class AsterixOptimizationContext extends AlgebricksOptimizationCont
 
     public void addDataSource(DataSource dataSource) {
         byte type = dataSource.getDatasourceType();
-        Set<DataSourceId> set = dataSourceMap.get(type);
+        Set<DataSource> set = dataSourceMap.get(type);
         if (set == null) {
             set = new HashSet<>();
             dataSourceMap.put(type, set);
         }
-        set.add(dataSource.getId());
+        set.add(dataSource);
     }
 
-    public Int2ObjectMap<Set<DataSourceId>> getDataSourceMap() {
+    public Int2ObjectMap<Set<DataSource>> getDataSourceMap() {
         return dataSourceMap;
     }
 }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
index 916fd75..43136c0 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
@@ -142,7 +142,7 @@ public class DisjunctivePredicateToJoinRule implements IAlgebraicRewriteRule {
 
         ILogicalExpression cExp = new ConstantExpression(new AsterixConstantValue(list));
         Mutable<ILogicalExpression> mutCExp = new MutableObject<>(cExp);
-        IFunctionInfo scanFctInfo = BuiltinFunctions.getAsterixFunctionInfo(BuiltinFunctions.SCAN_COLLECTION);
+        IFunctionInfo scanFctInfo = BuiltinFunctions.getBuiltinFunctionInfo(BuiltinFunctions.SCAN_COLLECTION);
         UnnestingFunctionCallExpression scanExp = new UnnestingFunctionCallExpression(scanFctInfo, mutCExp);
         scanExp.setSourceLocation(sourceLoc);
         LogicalVariable scanVar = context.newVar();
@@ -151,7 +151,7 @@ public class DisjunctivePredicateToJoinRule implements IAlgebraicRewriteRule {
         unn.getInputs().add(new MutableObject<>(ets));
         context.computeAndSetTypeEnvironmentForOperator(unn);
 
-        IFunctionInfo eqFctInfo = BuiltinFunctions.getAsterixFunctionInfo(AlgebricksBuiltinFunctions.EQ);
+        IFunctionInfo eqFctInfo = BuiltinFunctions.getBuiltinFunctionInfo(AlgebricksBuiltinFunctions.EQ);
         AbstractFunctionCallExpression eqExp = new ScalarFunctionCallExpression(eqFctInfo);
         eqExp.setSourceLocation(sourceLoc);
         VariableReferenceExpression scanVarRef = new VariableReferenceExpression(scanVar);
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/FuzzyJoinRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/FuzzyJoinRule.java
index cc91c06..34a256b 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/FuzzyJoinRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/FuzzyJoinRule.java
@@ -21,15 +21,26 @@ package org.apache.asterix.optimizer.rules;
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.function.BiFunction;
 
 import org.apache.asterix.aqlplus.parser.AQLPlusParser;
 import org.apache.asterix.aqlplus.parser.ParseException;
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.functions.FunctionSignature;
+import org.apache.asterix.lang.aql.clause.JoinClause;
+import org.apache.asterix.lang.aql.clause.MetaVariableClause;
+import org.apache.asterix.lang.aql.expression.MetaVariableExpr;
+import org.apache.asterix.lang.aql.rewrites.visitor.AqlFunctionCallResolverVisitor;
+import org.apache.asterix.lang.aql.visitor.base.IAQLPlusVisitor;
 import org.apache.asterix.lang.common.base.Clause;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.statement.FunctionDecl;
 import org.apache.asterix.lang.common.struct.VarIdentifier;
 import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.metadata.declared.MetadataProvider;
@@ -427,6 +438,12 @@ public class FuzzyJoinRule implements IAlgebraicRewriteRule {
             throw CompilationException.create(ErrorCode.COMPILATION_TRANSLATION_ERROR, e);
         }
 
+        AqlPlusFunctionCallResolverVisitor fnResolverVisitor =
+                new AqlPlusFunctionCallResolverVisitor(metadataProvider, Collections.emptyList());
+        for (Clause clause : clauses) {
+            clause.accept(fnResolverVisitor, null);
+        }
+
         // Step 4. The essential substitution with translator.
         ILogicalPlan plan;
         try {
@@ -501,4 +518,40 @@ public class FuzzyJoinRule implements IAlgebraicRewriteRule {
         }
         return new ArrayList<>(primaryKeys);
     }
+
+    private static final class AqlPlusFunctionCallResolverVisitor extends AqlFunctionCallResolverVisitor
+            implements IAQLPlusVisitor<Expression, ILangExpression> {
+        private AqlPlusFunctionCallResolverVisitor(MetadataProvider metadataProvider,
+                List<FunctionDecl> declaredFunctions) {
+            super(metadataProvider, declaredFunctions);
+        }
+
+        @Override
+        protected BiFunction<String, Integer, FunctionSignature> createBuiltinFunctionResolver(
+                MetadataProvider metadataProvider) {
+            return FunctionUtil.createBuiltinFunctionResolver(true);
+        }
+
+        @Override
+        public Expression visitJoinClause(JoinClause joinClause, ILangExpression arg) throws CompilationException {
+            for (Clause clause : joinClause.getLeftClauses()) {
+                clause.accept(this, arg);
+            }
+            for (Clause clause : joinClause.getRightClauses()) {
+                clause.accept(this, arg);
+            }
+            joinClause.getWhereExpr().accept(this, arg);
+            return null;
+        }
+
+        @Override
+        public Expression visitMetaVariableClause(MetaVariableClause c, ILangExpression arg) {
+            return null;
+        }
+
+        @Override
+        public Expression visitMetaVariableExpr(MetaVariableExpr v, ILangExpression arg) {
+            return v;
+        }
+    }
 }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
index 8f5bd9c..d3eb0c2 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
@@ -19,7 +19,6 @@
 
 package org.apache.asterix.optimizer.rules;
 
-import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.functions.ExternalFunctionInfo;
 import org.apache.asterix.om.typecomputer.base.TypeCastUtils;
@@ -69,7 +68,7 @@ public class IntroduceDynamicTypeCastForExternalFunctionRule implements IAlgebra
             }
         }
         // if the current function is builtin function, skip the type casting
-        if (BuiltinFunctions.getBuiltinFunctionIdentifier(funcCallExpr.getFunctionIdentifier()) != null) {
+        if (BuiltinFunctions.getBuiltinFunctionInfo(funcCallExpr.getFunctionIdentifier()) != null) {
             return changed;
         }
         IAType inputType;
@@ -97,8 +96,8 @@ public class IntroduceDynamicTypeCastForExternalFunctionRule implements IAlgebra
                 checkUnknown = true;
             }
             if (castFlag || checkUnknown) {
-                AbstractFunctionCallExpression castFunc =
-                        new ScalarFunctionCallExpression(FunctionUtil.getFunctionInfo(BuiltinFunctions.CAST_TYPE));
+                AbstractFunctionCallExpression castFunc = new ScalarFunctionCallExpression(
+                        BuiltinFunctions.getBuiltinFunctionInfo(BuiltinFunctions.CAST_TYPE));
                 castFunc.setSourceLocation(argExpr.getValue().getSourceLocation());
                 castFunc.getArguments().add(argExpr);
                 TypeCastUtils.setRequiredAndInputTypes(castFunc, reqArgType, inputType);
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/RewriteDistinctAggregateRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/RewriteDistinctAggregateRule.java
index 92b1ab6..9d0ee59 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/RewriteDistinctAggregateRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/RewriteDistinctAggregateRule.java
@@ -321,7 +321,7 @@ public final class RewriteDistinctAggregateRule implements IAlgebraicRewriteRule
             if (regularAggForDistinct == null) {
                 throw new IllegalStateException(String.valueOf(callExpr.getFunctionIdentifier()));
             }
-            callExpr.setFunctionInfo(BuiltinFunctions.getAsterixFunctionInfo(regularAggForDistinct));
+            callExpr.setFunctionInfo(BuiltinFunctions.getBuiltinFunctionInfo(regularAggForDistinct));
 
             if (assignOp != null) {
                 callExpr.getArguments().get(0).setValue(distinctVarRef.cloneExpression());
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixMemoryRequirementsRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixMemoryRequirementsRule.java
index f4f8fa2..abe4b71 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixMemoryRequirementsRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixMemoryRequirementsRule.java
@@ -22,9 +22,8 @@ package org.apache.asterix.optimizer.rules;
 import java.util.Set;
 import java.util.function.Predicate;
 
-import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.metadata.declared.DataSource;
-import org.apache.asterix.metadata.declared.DataSourceId;
+import org.apache.asterix.metadata.declared.FunctionDataSource;
 import org.apache.asterix.metadata.utils.MetadataConstants;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.optimizer.base.AsterixOptimizationContext;
@@ -53,13 +52,13 @@ public final class SetAsterixMemoryRequirementsRule extends SetMemoryRequirement
     }
 
     private boolean forceMinMemoryBudget(AsterixOptimizationContext context) {
-        Int2ObjectMap<Set<DataSourceId>> dataSourceMap = context.getDataSourceMap();
+        Int2ObjectMap<Set<DataSource>> dataSourceMap = context.getDataSourceMap();
         if (dataSourceMap.isEmpty()) {
             return false;
         }
-        for (Int2ObjectMap.Entry<Set<DataSourceId>> me : dataSourceMap.int2ObjectEntrySet()) {
+        for (Int2ObjectMap.Entry<Set<DataSource>> me : dataSourceMap.int2ObjectEntrySet()) {
             int dataSourceType = me.getIntKey();
-            Predicate<DataSourceId> dataSourceTest;
+            Predicate<DataSource> dataSourceTest;
             switch (dataSourceType) {
                 case DataSource.Type.INTERNAL_DATASET:
                     dataSourceTest = SetAsterixMemoryRequirementsRule::isMinMemoryBudgetDataset;
@@ -77,13 +76,13 @@ public final class SetAsterixMemoryRequirementsRule extends SetMemoryRequirement
         return true;
     }
 
-    private static boolean isMinMemoryBudgetDataset(DataSourceId dsId) {
-        return MetadataConstants.METADATA_DATAVERSE_NAME.equals(dsId.getDataverseName());
+    private static boolean isMinMemoryBudgetDataset(DataSource ds) {
+        return MetadataConstants.METADATA_DATAVERSE_NAME.equals(ds.getId().getDataverseName());
     }
 
-    private static boolean isMinMemoryBudgetFunction(DataSourceId dsId) {
-        return BuiltinFunctions.builtinFunctionHasProperty(
-                FunctionSignature.createFunctionIdentifier(dsId.getDataverseName(), dsId.getDatasourceName()),
+    private static boolean isMinMemoryBudgetFunction(DataSource ds) {
+        FunctionDataSource fds = (FunctionDataSource) ds;
+        return BuiltinFunctions.builtinFunctionHasProperty(fds.getFunctionId(),
                 BuiltinFunctions.DataSourceFunctionProperty.MIN_MEMORY_BUDGET);
     }
 }
\ No newline at end of file
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/UnnestToDataScanRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/UnnestToDataScanRule.java
index 0d02385..eabe977 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/UnnestToDataScanRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/UnnestToDataScanRule.java
@@ -19,6 +19,7 @@
 package org.apache.asterix.optimizer.rules;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionToDataSourceRewriter;
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
@@ -46,7 +47,12 @@ public class UnnestToDataScanRule implements IAlgebraicRewriteRule {
         if (f == null) {
             return false;
         }
-        return BuiltinFunctions.getDatasourceTransformer(f.getFunctionIdentifier()).rewrite(opRef, context);
+        IFunctionToDataSourceRewriter transformer =
+                BuiltinFunctions.getDatasourceTransformer(f.getFunctionIdentifier());
+        if (transformer == null) {
+            return false;
+        }
+        return transformer.rewrite(opRef, context);
     }
 
     public static AbstractFunctionCallExpression getFunctionCall(Mutable<ILogicalOperator> opRef) {
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
index 4bb1adf..17bc77c 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
@@ -36,7 +36,6 @@ import org.apache.asterix.common.config.MetadataProperties;
 import org.apache.asterix.common.dataflow.ICcApplicationContext;
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
-import org.apache.asterix.common.functions.FunctionConstants;
 import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.lang.common.base.Expression;
@@ -90,8 +89,8 @@ import org.apache.asterix.om.base.AInt64;
 import org.apache.asterix.om.base.AString;
 import org.apache.asterix.om.base.IAObject;
 import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.functions.BuiltinFunctionInfo;
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.FunctionInfo;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
@@ -883,7 +882,7 @@ abstract class LangExpressionToPlanTranslator
             List<Mutable<ILogicalExpression>> args, SourceLocation sourceLoc) throws CompilationException {
         AbstractFunctionCallExpression f;
         if ((f = lookupUserDefinedFunction(signature, args, sourceLoc)) == null) {
-            f = lookupBuiltinFunction(signature.getName(), signature.getArity(), args, sourceLoc);
+            f = lookupBuiltinFunction(signature, args, sourceLoc);
         }
         return f;
     }
@@ -895,24 +894,25 @@ abstract class LangExpressionToPlanTranslator
             if (function == null) {
                 return null;
             }
-            IFunctionInfo finfo = function.isExternal()
-                    ? ExternalFunctionCompilerUtil.getExternalFunctionInfo(metadataProvider, function)
-                    : FunctionUtil.getFunctionInfo(signature);
+            if (!function.isExternal()) {
+                // all non-external UDFs should've been inlined by now
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc, signature);
+            }
+            IFunctionInfo finfo = ExternalFunctionCompilerUtil.getExternalFunctionInfo(metadataProvider, function);
             AbstractFunctionCallExpression f = new ScalarFunctionCallExpression(finfo, args);
             f.setSourceLocation(sourceLoc);
             return f;
+        } catch (CompilationException e) {
+            throw e;
         } catch (AlgebricksException e) {
-            throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLoc, e.getMessage(), e);
+            throw new CompilationException(ErrorCode.COMPILATION_ERROR, e, sourceLoc, e.getMessage());
         }
     }
 
-    private AbstractFunctionCallExpression lookupBuiltinFunction(String functionName, int arity,
-            List<Mutable<ILogicalExpression>> args, SourceLocation sourceLoc) {
+    private AbstractFunctionCallExpression lookupBuiltinFunction(FunctionSignature signature,
+            List<Mutable<ILogicalExpression>> args, SourceLocation sourceLoc) throws CompilationException {
         AbstractFunctionCallExpression f;
-        FunctionIdentifier fi = getBuiltinFunctionIdentifier(functionName, arity);
-        if (fi == null) {
-            return null;
-        }
+        FunctionIdentifier fi = signature.createFunctionIdentifier();
         if (BuiltinFunctions.isBuiltinAggregateFunction(fi)) {
             f = BuiltinFunctions.makeAggregateFunctionExpression(fi, args);
         } else if (BuiltinFunctions.isBuiltinUnnestingFunction(fi)) {
@@ -923,29 +923,17 @@ abstract class LangExpressionToPlanTranslator
         } else if (BuiltinFunctions.isWindowFunction(fi)) {
             f = BuiltinFunctions.makeWindowFunctionExpression(fi, args);
         } else {
-            f = new ScalarFunctionCallExpression(FunctionUtil.getFunctionInfo(fi), args);
+            BuiltinFunctionInfo finfo = FunctionUtil.getFunctionInfo(fi);
+            if (finfo == null) {
+                // should've been resolved earlier
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc, fi);
+            }
+            f = new ScalarFunctionCallExpression(finfo, args);
         }
         f.setSourceLocation(sourceLoc);
         return f;
     }
 
-    protected FunctionIdentifier getBuiltinFunctionIdentifier(String functionName, int arity) {
-        FunctionIdentifier fi = new FunctionIdentifier(AlgebricksBuiltinFunctions.ALGEBRICKS_NS, functionName, arity);
-        FunctionInfo afi = BuiltinFunctions.lookupFunction(fi);
-        FunctionIdentifier builtinAquafi = afi == null ? null : afi.getFunctionIdentifier();
-
-        if (builtinAquafi != null) {
-            fi = builtinAquafi;
-        } else {
-            fi = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, functionName, arity);
-            afi = BuiltinFunctions.lookupFunction(fi);
-            if (afi == null) {
-                return null;
-            }
-        }
-        return fi;
-    }
-
     @Override
     public Pair<ILogicalOperator, LogicalVariable> visit(FunctionDecl fd, Mutable<ILogicalOperator> tupSource) {
         throw new IllegalStateException("Function declarations should be inlined at AST rewriting phase.");
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
index c3fc56c..c394722 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
@@ -113,6 +113,7 @@ import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCall
 import org.apache.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestNonMapOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestOperator;
@@ -1071,11 +1072,12 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
         List<Expression> fargs = winExpr.getExprList();
 
         FunctionSignature fs = winExpr.getFunctionSignature();
-        FunctionIdentifier fi = getBuiltinFunctionIdentifier(fs.getName(), fs.getArity());
-        if (fi == null) {
+        IFunctionInfo finfo = BuiltinFunctions.getBuiltinFunctionInfo(fs.createFunctionIdentifier());
+        if (finfo == null) {
             throw new CompilationException(ErrorCode.COMPILATION_EXPECTED_WINDOW_FUNCTION, winExpr.getSourceLocation(),
                     fs.getName());
         }
+        FunctionIdentifier fi = finfo.getFunctionIdentifier();
         boolean isWin = BuiltinFunctions.isWindowFunction(fi);
         boolean isWinAgg = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
                 BuiltinFunctions.WindowFunctionProperty.HAS_LIST_ARG);
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/ActiveRequestsDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/ActiveRequestsDatasource.java
index 2afd11c..24a127a 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/ActiveRequestsDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/ActiveRequestsDatasource.java
@@ -33,7 +33,7 @@ public class ActiveRequestsDatasource extends FunctionDataSource {
             createDataSourceId(ActiveRequestsRewriter.ACTIVE_REQUESTS);
 
     public ActiveRequestsDatasource(INodeDomain domain) throws AlgebricksException {
-        super(ACTIVE_REQUESTS_DATASOURCE_ID, domain);
+        super(ACTIVE_REQUESTS_DATASOURCE_ID, ActiveRequestsRewriter.ACTIVE_REQUESTS, domain);
     }
 
     @Override
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/CompletedRequestsDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/CompletedRequestsDatasource.java
index 1fe103e..0bfbff1 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/CompletedRequestsDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/CompletedRequestsDatasource.java
@@ -33,7 +33,7 @@ public class CompletedRequestsDatasource extends FunctionDataSource {
             createDataSourceId(CompletedRequestsRewriter.COMPLETED_REQUESTS);
 
     public CompletedRequestsDatasource(INodeDomain domain) throws AlgebricksException {
-        super(COMPLETED_REQUESTS_DATASOURCE_ID, domain);
+        super(COMPLETED_REQUESTS_DATASOURCE_ID, CompletedRequestsRewriter.COMPLETED_REQUESTS, domain);
     }
 
     @Override
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetResourcesDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetResourcesDatasource.java
index ffd04ab..3e5033b 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetResourcesDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetResourcesDatasource.java
@@ -34,7 +34,7 @@ public class DatasetResourcesDatasource extends FunctionDataSource {
     private final int datasetId;
 
     public DatasetResourcesDatasource(INodeDomain domain, int datasetId) throws AlgebricksException {
-        super(DATASET_RESOURCES_DATASOURCE_ID, domain);
+        super(DATASET_RESOURCES_DATASOURCE_ID, DatasetResourcesRewriter.DATASET_RESOURCES, domain);
         this.datasetId = datasetId;
     }
 
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DumpIndexDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DumpIndexDatasource.java
index 04e3eea..6318f8e 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DumpIndexDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DumpIndexDatasource.java
@@ -39,7 +39,7 @@ public class DumpIndexDatasource extends FunctionDataSource {
 
     public DumpIndexDatasource(INodeDomain domain, IndexDataflowHelperFactory indexDataflowHelperFactory,
             RecordDescriptor recDesc, IBinaryComparatorFactory[] comparatorFactories) throws AlgebricksException {
-        super(DUMP_INDEX_DATASOURCE_ID, domain);
+        super(DUMP_INDEX_DATASOURCE_ID, DumpIndexRewriter.DUMP_INDEX, domain);
         this.indexDataflowHelperFactory = indexDataflowHelperFactory;
         this.recDesc = recDesc;
         this.comparatorFactories = comparatorFactories;
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/JobSummariesDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/JobSummariesDatasource.java
index 835a97d..6bab0cd 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/JobSummariesDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/JobSummariesDatasource.java
@@ -32,7 +32,7 @@ public class JobSummariesDatasource extends FunctionDataSource {
             createDataSourceId(JobSummariesRewriter.JOBSUMMARIES);
 
     public JobSummariesDatasource(INodeDomain domain) throws AlgebricksException {
-        super(JOB_SUMMARIES_DATASOURCE_ID, domain);
+        super(JOB_SUMMARIES_DATASOURCE_ID, JobSummariesRewriter.JOBSUMMARIES, domain);
     }
 
     @Override
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/PingDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/PingDatasource.java
index 67d5e53..1b6d807 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/PingDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/PingDatasource.java
@@ -31,7 +31,7 @@ public class PingDatasource extends FunctionDataSource {
     private static final DataSourceId PING_DATASOURCE_ID = createDataSourceId(PingRewriter.PING);
 
     public PingDatasource(INodeDomain domain) throws AlgebricksException {
-        super(PING_DATASOURCE_ID, domain);
+        super(PING_DATASOURCE_ID, PingRewriter.PING, domain);
     }
 
     @Override
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsDatasource.java
index b8a4167..6157412 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsDatasource.java
@@ -34,7 +34,7 @@ public class StorageComponentsDatasource extends FunctionDataSource {
     private final int datasetId;
 
     public StorageComponentsDatasource(INodeDomain domain, int datasetId) throws AlgebricksException {
-        super(STORAGE_COMPONENTS_DATASOURCE_ID, domain);
+        super(STORAGE_COMPONENTS_DATASOURCE_ID, StorageComponentsRewriter.STORAGE_COMPONENTS, domain);
         this.datasetId = datasetId;
     }
 
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSAllTablesDataGeneratorDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSAllTablesDataGeneratorDatasource.java
index c382fb4..82303fc 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSAllTablesDataGeneratorDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSAllTablesDataGeneratorDatasource.java
@@ -35,13 +35,11 @@ import org.apache.hyracks.algebricks.core.algebra.properties.INodeDomain;
 public class TPCDSAllTablesDataGeneratorDatasource extends FunctionDataSource {
 
     private final double scalingFactor;
-    private final FunctionIdentifier functionIdentifier;
 
     TPCDSAllTablesDataGeneratorDatasource(INodeDomain domain, double scalingFactor,
             FunctionIdentifier functionIdentifier) throws AlgebricksException {
-        super(createDataSourceId(scalingFactor), domain);
+        super(createDataSourceId(scalingFactor), functionIdentifier, domain);
         this.scalingFactor = scalingFactor;
-        this.functionIdentifier = functionIdentifier;
     }
 
     /**
@@ -61,7 +59,7 @@ public class TPCDSAllTablesDataGeneratorDatasource extends FunctionDataSource {
     @Override
     protected IDatasourceFunction createFunction(MetadataProvider metadataProvider,
             AlgebricksAbsolutePartitionConstraint locations) {
-        return new TPCDSDataGeneratorFunction(locations, null, scalingFactor, functionIdentifier);
+        return new TPCDSDataGeneratorFunction(locations, null, scalingFactor, functionId);
     }
 
     @Override
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSSingleTableDataGeneratorDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSSingleTableDataGeneratorDatasource.java
index df3903f..458bff6 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSSingleTableDataGeneratorDatasource.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/TPCDSSingleTableDataGeneratorDatasource.java
@@ -36,14 +36,12 @@ public class TPCDSSingleTableDataGeneratorDatasource extends FunctionDataSource
 
     private final String tableName;
     private final double scalingFactor;
-    private final FunctionIdentifier functionIdentifier;
 
     TPCDSSingleTableDataGeneratorDatasource(INodeDomain domain, String tableName, double scalingFactor,
             FunctionIdentifier functionIdentifier) throws AlgebricksException {
-        super(createDataSourceId(tableName, scalingFactor), domain);
+        super(createDataSourceId(tableName, scalingFactor), functionIdentifier, domain);
         this.tableName = tableName;
         this.scalingFactor = scalingFactor;
-        this.functionIdentifier = functionIdentifier;
     }
 
     /**
@@ -65,7 +63,7 @@ public class TPCDSSingleTableDataGeneratorDatasource extends FunctionDataSource
     @Override
     protected IDatasourceFunction createFunction(MetadataProvider metadataProvider,
             AlgebricksAbsolutePartitionConstraint locations) {
-        return new TPCDSDataGeneratorFunction(locations, tableName, scalingFactor, functionIdentifier);
+        return new TPCDSDataGeneratorFunction(locations, tableName, scalingFactor, functionId);
     }
 
     @Override
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index 46a3dc6..dd1f193 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -673,6 +673,8 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
         boolean bActiveTxn = true;
         metadataProvider.setMetadataTxnContext(mdTxnCtx);
         Dataset dataset = null;
+        Datatype itemTypeEntity = null, metaItemTypeEntity = null;
+        boolean itemTypeAdded = false, metaItemTypeAdded = false;
         try {
             // Check if the dataverse exists
             Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverseName);
@@ -690,8 +692,9 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                     throw new CompilationException(ErrorCode.DATASET_EXISTS, sourceLoc, datasetName, dataverseName);
                 }
             }
-            Datatype itemTypeEntity;
+
             IAType itemType;
+            boolean itemTypeIsInline = false;
             switch (itemTypeExpr.getTypeKind()) {
                 case TYPEREFERENCE:
                     itemTypeEntity = metadataProvider.findTypeEntity(itemTypeDataverseName, itemTypeName);
@@ -707,7 +710,7 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                     itemType = translateType(itemTypeDataverseName, itemTypeName, itemTypeExpr, mdTxnCtx);
                     validateDatasetItemType(dsType, itemType, false, sourceLoc);
                     itemTypeEntity = new Datatype(itemTypeDataverseName, itemTypeName, itemType, true);
-                    MetadataManager.INSTANCE.addDatatype(mdTxnCtx, itemTypeEntity);
+                    itemTypeIsInline = true;
                     break;
                 default:
                     throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc,
@@ -723,13 +726,15 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
             } else {
                 validateCompactionPolicy(compactionPolicy, compactionPolicyProperties, mdTxnCtx, false, sourceLoc);
             }
+
+            IAType metaItemType = null;
+            boolean metaItemTypeIsInline = false;
             switch (dsType) {
                 case INTERNAL:
-                    IAType metaItemType = null;
                     if (metaItemTypeExpr != null) {
                         switch (metaItemTypeExpr.getTypeKind()) {
                             case TYPEREFERENCE:
-                                Datatype metaItemTypeEntity =
+                                metaItemTypeEntity =
                                         metadataProvider.findTypeEntity(metaItemTypeDataverseName, metaItemTypeName);
                                 if (metaItemTypeEntity == null || metaItemTypeEntity.getIsAnonymous()) {
                                     // anonymous types cannot be referred from CREATE DATASET
@@ -743,8 +748,9 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                                 metaItemType = translateType(metaItemTypeDataverseName, metaItemTypeName,
                                         metaItemTypeExpr, mdTxnCtx);
                                 validateDatasetItemType(dsType, metaItemType, true, sourceLoc);
-                                MetadataManager.INSTANCE.addDatatype(mdTxnCtx,
-                                        new Datatype(metaItemTypeDataverseName, metaItemTypeName, metaItemType, true));
+                                metaItemTypeEntity =
+                                        new Datatype(metaItemTypeDataverseName, metaItemTypeName, metaItemType, true);
+                                metaItemTypeIsInline = true;
                                 break;
                             default:
                                 throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc,
@@ -803,6 +809,16 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                     datasetDetails, dd.getHints(), dsType, DatasetIdFactory.generateDatasetId(),
                     MetadataUtil.PENDING_ADD_OP, compressionScheme);
             MetadataManager.INSTANCE.addDataset(metadataProvider.getMetadataTxnContext(), dataset);
+
+            if (itemTypeIsInline) {
+                MetadataManager.INSTANCE.addDatatype(mdTxnCtx, itemTypeEntity);
+                itemTypeAdded = true;
+            }
+            if (metaItemTypeIsInline) {
+                MetadataManager.INSTANCE.addDatatype(mdTxnCtx, metaItemTypeEntity);
+                metaItemTypeAdded = true;
+            }
+
             if (dsType == DatasetType.INTERNAL) {
                 JobSpecification jobSpec = DatasetUtil.createDatasetJobSpec(dataset, metadataProvider);
 
@@ -858,8 +874,15 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
                 metadataProvider.setMetadataTxnContext(mdTxnCtx);
                 try {
-                    MetadataManager.INSTANCE.dropDataset(metadataProvider.getMetadataTxnContext(), dataverseName,
-                            datasetName);
+                    MetadataManager.INSTANCE.dropDataset(mdTxnCtx, dataverseName, datasetName);
+                    if (itemTypeAdded) {
+                        MetadataManager.INSTANCE.dropDatatype(mdTxnCtx, itemTypeEntity.getDataverseName(),
+                                itemTypeEntity.getDatatypeName());
+                    }
+                    if (metaItemTypeAdded) {
+                        MetadataManager.INSTANCE.dropDatatype(mdTxnCtx, metaItemTypeEntity.getDataverseName(),
+                                metaItemTypeEntity.getDatatypeName());
+                    }
                     MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
                 } catch (Exception e2) {
                     e.addSuppressed(e2);
@@ -1664,38 +1687,9 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
             }
             validateDatasetState(metadataProvider, ds, sourceLoc);
 
-            // prepare to drop item and meta types if they were created as inline types
-            DataverseName itemTypeDataverseName = ds.getItemTypeDataverseName();
-            String itemTypeName = ds.getItemTypeName();
-            boolean isInlineItemType = TypeUtil.isDatasetInlineTypeName(ds, itemTypeDataverseName, itemTypeName);
-            if (isInlineItemType) {
-                lockUtil.dropTypeBegin(lockManager, metadataProvider.getLocks(), itemTypeDataverseName, itemTypeName);
-            }
-            DataverseName metaTypeDataverseName = ds.getMetaItemTypeDataverseName();
-            String metaTypeName = ds.getMetaItemTypeName();
-            boolean isInlineMetaType =
-                    metaTypeName != null && TypeUtil.isDatasetInlineTypeName(ds, metaTypeDataverseName, metaTypeName);
-            if (isInlineMetaType) {
-                lockUtil.dropTypeBegin(lockManager, metadataProvider.getLocks(), metaTypeDataverseName, metaTypeName);
-            }
-            Datatype inlineItemType = isInlineItemType
-                    ? MetadataManager.INSTANCE.getDatatype(mdTxnCtx.getValue(), itemTypeDataverseName, itemTypeName)
-                    : null;
-            Datatype inlineMetaType = isInlineMetaType
-                    ? MetadataManager.INSTANCE.getDatatype(mdTxnCtx.getValue(), metaTypeDataverseName, metaTypeName)
-                    : null;
-
             ds.drop(metadataProvider, mdTxnCtx, jobsToExecute, bActiveTxn, progress, hcc, dropCorrespondingNodeGroup,
                     sourceLoc);
 
-            // drop inline item and meta types
-            if (isInlineItemType && inlineItemType.getIsAnonymous()) {
-                MetadataManager.INSTANCE.dropDatatype(mdTxnCtx.getValue(), itemTypeDataverseName, itemTypeName);
-            }
-            if (isInlineMetaType && inlineMetaType.getIsAnonymous()) {
-                MetadataManager.INSTANCE.dropDatatype(mdTxnCtx.getValue(), metaTypeDataverseName, metaTypeName);
-            }
-
             MetadataManager.INSTANCE.commitTransaction(mdTxnCtx.getValue());
             return true;
         } catch (Exception e) {
@@ -2011,8 +2005,8 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
     }
 
     protected void doCreateFunction(MetadataProvider metadataProvider, CreateFunctionStatement cfs,
-            FunctionSignature signature, IStatementRewriter stmtRewriter) throws Exception {
-        DataverseName dataverseName = signature.getDataverseName();
+            FunctionSignature functionSignature, IStatementRewriter stmtRewriter) throws Exception {
+        DataverseName dataverseName = functionSignature.getDataverseName();
         SourceLocation sourceLoc = cfs.getSourceLocation();
         MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
         metadataProvider.setMetadataTxnContext(mdTxnCtx);
@@ -2021,45 +2015,85 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
             if (dv == null) {
                 throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverseName);
             }
-            Function function = MetadataManager.INSTANCE.getFunction(mdTxnCtx, signature);
-            if (function != null) {
-                if (cfs.getIfNotExists()) {
+            List<TypeSignature> existingInlineTypes;
+            Function existingFunction = MetadataManager.INSTANCE.getFunction(mdTxnCtx, functionSignature);
+            if (existingFunction != null) {
+                if (cfs.getReplaceIfExists()) {
+                    if (cfs.getIfNotExists()) {
+                        throw new CompilationException(ErrorCode.PARSE_ERROR, cfs.getSourceLocation(), "IF NOT EXISTS");
+                    }
+                } else if (cfs.getIfNotExists()) {
                     MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
                     return;
+                } else {
+                    throw new CompilationException(ErrorCode.FUNCTION_EXISTS, cfs.getSourceLocation(),
+                            functionSignature.toString(false));
                 }
-                throw new CompilationException(ErrorCode.FUNCTION_EXISTS, cfs.getSourceLocation(),
-                        signature.toString(false));
-            }
-
-            boolean isExternal = cfs.isExternal();
-            List<Pair<VarIdentifier, TypeExpression>> paramList = cfs.getParameters();
-            int paramCount = paramList.size();
-            List<VarIdentifier> paramVars = new ArrayList<>(paramCount);
-            List<String> paramNames = new ArrayList<>(paramCount);
-            List<TypeSignature> paramTypes = new ArrayList<>(paramCount);
-            LinkedHashSet<TypeSignature> dependentTypes = new LinkedHashSet<>();
-
-            for (int i = 0; i < paramCount; i++) {
-                Pair<VarIdentifier, TypeExpression> paramPair = paramList.get(i);
-                VarIdentifier paramName = paramPair.getFirst();
-                TypeExpression paramTypeExpr = paramPair.getSecond();
-                Pair<TypeSignature, TypeSignature> paramType = translateFunctionParameterType(signature, i,
-                        paramTypeExpr, isExternal, sourceLoc, metadataProvider, mdTxnCtx);
-                paramVars.add(paramName);
-                paramNames.add(stmtRewriter.toFunctionParameterName(paramName));
-                paramTypes.add(paramType.first);
-                if (paramType.second != null) {
-                    dependentTypes.add(paramType.second);
+                existingInlineTypes = TypeUtil.getFunctionInlineTypes(existingFunction);
+            } else {
+                existingInlineTypes = Collections.emptyList();
+            }
+
+            Map<TypeSignature, Datatype> newInlineTypes;
+            Function function;
+            if (cfs.isExternal()) {
+                List<Pair<VarIdentifier, TypeExpression>> paramList = cfs.getParameters();
+                int paramCount = paramList.size();
+                List<String> paramNames = new ArrayList<>(paramCount);
+                List<TypeSignature> paramTypes = new ArrayList<>(paramCount);
+                LinkedHashSet<TypeSignature> depTypes = new LinkedHashSet<>();
+                newInlineTypes = new HashMap<>();
+
+                for (int i = 0; i < paramCount; i++) {
+                    Pair<VarIdentifier, TypeExpression> paramPair = paramList.get(i);
+                    TypeSignature paramTypeSignature;
+                    TypeSignature paramDepTypeSignature;
+                    Datatype paramInlineTypeEntity;
+                    TypeExpression paramTypeExpr = paramPair.getSecond();
+                    if (paramTypeExpr != null) {
+                        Triple<TypeSignature, TypeSignature, Datatype> paramTypeInfo = translateFunctionParameterType(
+                                functionSignature, i, paramTypeExpr, sourceLoc, metadataProvider, mdTxnCtx);
+                        paramTypeSignature = paramTypeInfo.first;
+                        paramDepTypeSignature = paramTypeInfo.second;
+                        paramInlineTypeEntity = paramTypeInfo.third;
+                    } else {
+                        paramTypeSignature = null; // == any
+                        paramDepTypeSignature = null;
+                        paramInlineTypeEntity = null;
+                    }
+                    paramTypes.add(paramTypeSignature); // null == any
+                    if (paramDepTypeSignature != null) {
+                        depTypes.add(paramDepTypeSignature);
+                    }
+                    if (paramInlineTypeEntity != null) {
+                        newInlineTypes.put(paramTypeSignature, paramInlineTypeEntity);
+                    }
+                    VarIdentifier paramName = paramPair.getFirst();
+                    paramNames.add(stmtRewriter.toFunctionParameterName(paramName));
                 }
-            }
 
-            if (isExternal) {
+                TypeSignature returnTypeSignature;
+                TypeSignature returnDepTypeSignature;
+                Datatype returnInlineTypeEntity;
                 TypeExpression returnTypeExpr = cfs.getReturnType();
-                Pair<TypeSignature, TypeSignature> returnType = translateFunctionParameterType(signature, -1,
-                        returnTypeExpr, isExternal, sourceLoc, metadataProvider, mdTxnCtx);
-                if (returnType.second != null) {
-                    dependentTypes.add(returnType.second);
+                if (returnTypeExpr != null) {
+                    Triple<TypeSignature, TypeSignature, Datatype> returnTypeInfo = translateFunctionParameterType(
+                            functionSignature, -1, returnTypeExpr, sourceLoc, metadataProvider, mdTxnCtx);
+                    returnTypeSignature = returnTypeInfo.first;
+                    returnDepTypeSignature = returnTypeInfo.second;
+                    returnInlineTypeEntity = returnTypeInfo.third;
+                } else {
+                    returnTypeSignature = null; // == any
+                    returnDepTypeSignature = null;
+                    returnInlineTypeEntity = null;
+                }
+                if (returnDepTypeSignature != null) {
+                    depTypes.add(returnDepTypeSignature);
                 }
+                if (returnInlineTypeEntity != null) {
+                    newInlineTypes.put(returnTypeSignature, returnInlineTypeEntity);
+                }
+
                 DataverseName libraryDataverseName = cfs.getLibraryDataverseName();
                 if (libraryDataverseName == null) {
                     libraryDataverseName = dataverseName;
@@ -2075,14 +2109,28 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 List<String> externalIdentifier = cfs.getExternalIdentifier();
                 ExternalFunctionCompilerUtil.validateExternalIdentifier(externalIdentifier, language,
                         cfs.getSourceLocation());
-
                 List<List<Triple<DataverseName, String, String>>> dependencies =
-                        FunctionUtil.getExternalFunctionDependencies(dependentTypes);
-                function = new Function(signature, paramNames, paramTypes, returnType.first, null,
+                        FunctionUtil.getExternalFunctionDependencies(depTypes);
+
+                function = new Function(functionSignature, paramNames, paramTypes, returnTypeSignature, null,
                         FunctionKind.SCALAR.toString(), library.getLanguage(), libraryDataverseName, libraryName,
                         externalIdentifier, cfs.getNullCall(), cfs.getDeterministic(), cfs.getResources(),
                         dependencies);
             } else {
+                List<Pair<VarIdentifier, TypeExpression>> paramList = cfs.getParameters();
+                int paramCount = paramList.size();
+                List<VarIdentifier> paramVars = new ArrayList<>(paramCount);
+                List<String> paramNames = new ArrayList<>(paramCount);
+                for (Pair<VarIdentifier, TypeExpression> paramPair : paramList) {
+                    VarIdentifier paramName = paramPair.getFirst();
+                    paramVars.add(paramName);
+                    paramNames.add(stmtRewriter.toFunctionParameterName(paramName));
+                    if (paramPair.getSecond() != null) {
+                        throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc,
+                                paramName.toString());
+                    }
+                }
+
                 //Check whether the function is use-able
                 metadataProvider.setDefaultDataverse(dv);
                 Query wrappedQuery = new Query(false);
@@ -2091,18 +2139,40 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 wrappedQuery.setTopLevel(false);
                 apiFramework.reWriteQuery(declaredFunctions, metadataProvider, wrappedQuery, sessionOutput, false,
                         paramVars, warningCollector);
-                List<List<Triple<DataverseName, String, String>>> dependencies =
-                        FunctionUtil.getFunctionDependencies(rewriterFactory.createQueryRewriter(),
-                                cfs.getFunctionBodyExpression(), metadataProvider, dependentTypes);
-                function = new Function(signature, paramNames, paramTypes, TypeUtil.ANY_TYPE_SIGNATURE,
-                        cfs.getFunctionBody(), FunctionKind.SCALAR.toString(),
-                        compilationProvider.getParserFactory().getLanguage(), null, null, null, null, null, null,
-                        dependencies);
+                List<List<Triple<DataverseName, String, String>>> dependencies = FunctionUtil.getFunctionDependencies(
+                        rewriterFactory.createQueryRewriter(), cfs.getFunctionBodyExpression(), metadataProvider);
+
+                newInlineTypes = Collections.emptyMap();
+                function = new Function(functionSignature, paramNames, null, null, cfs.getFunctionBody(),
+                        FunctionKind.SCALAR.toString(), compilationProvider.getParserFactory().getLanguage(), null,
+                        null, null, null, null, null, dependencies);
             }
 
-            MetadataManager.INSTANCE.addFunction(mdTxnCtx, function);
+            if (existingFunction == null) {
+                // add new function and its inline types
+                for (Datatype newInlineType : newInlineTypes.values()) {
+                    MetadataManager.INSTANCE.addDatatype(mdTxnCtx, newInlineType);
+                }
+                MetadataManager.INSTANCE.addFunction(mdTxnCtx, function);
+            } else {
+                // replace existing function and its inline types
+                for (TypeSignature existingInlineType : existingInlineTypes) {
+                    Datatype newInlineType =
+                            newInlineTypes.isEmpty() ? null : newInlineTypes.remove(existingInlineType);
+                    if (newInlineType == null) {
+                        MetadataManager.INSTANCE.dropDatatype(mdTxnCtx, existingInlineType.getDataverseName(),
+                                existingInlineType.getName());
+                    } else {
+                        MetadataManager.INSTANCE.updateDatatype(mdTxnCtx, newInlineType);
+                    }
+                }
+                for (Datatype inlineType : newInlineTypes.values()) {
+                    MetadataManager.INSTANCE.addDatatype(mdTxnCtx, inlineType);
+                }
+                MetadataManager.INSTANCE.updateFunction(mdTxnCtx, function);
+            }
             if (LOGGER.isInfoEnabled()) {
-                LOGGER.info("Installed function: " + signature);
+                LOGGER.info("Installed function: " + functionSignature);
             }
             MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
         } catch (Exception e) {
@@ -2111,20 +2181,11 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
         }
     }
 
-    private Pair<TypeSignature, TypeSignature> translateFunctionParameterType(FunctionSignature functionSignature,
-            int paramIdx, TypeExpression paramTypeExpr, boolean isExternalFunction, SourceLocation sourceLoc,
+    private Triple<TypeSignature, TypeSignature, Datatype> translateFunctionParameterType(
+            FunctionSignature functionSignature, int paramIdx, TypeExpression paramTypeExpr, SourceLocation sourceLoc,
             MetadataProvider metadataProvider, MetadataTransactionContext mdTxnCtx) throws AlgebricksException {
-        if (paramTypeExpr == null) {
-            return new Pair<>(TypeUtil.ANY_TYPE_SIGNATURE, null);
-        }
-
-        if (!isExternalFunction) {
-            // grammar doesn't allow parameter types for inline functions
-            throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc);
-        }
-
-        TypeSignature resultType, dependentType;
-
+        TypeSignature paramTypeSignature, depTypeSignature;
+        Datatype paramInlineTypeEntity = null;
         switch (paramTypeExpr.getTypeKind()) {
             case TYPEREFERENCE:
                 TypeReferenceExpression paramTypeRefExpr = (TypeReferenceExpression) paramTypeExpr;
@@ -2132,19 +2193,19 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 BuiltinType builtinType = BuiltinTypeMap.getBuiltinType(paramTypeName);
                 if (builtinType != null) {
                     // built-in type
-                    resultType = new TypeSignature(builtinType);
-                    dependentType = null;
+                    paramTypeSignature = new TypeSignature(builtinType);
+                    depTypeSignature = null;
                 } else {
                     // user-defined type
                     DataverseName paramTypeDataverseName = paramTypeRefExpr.getIdent().first;
                     if (paramTypeDataverseName == null) {
                         paramTypeDataverseName = functionSignature.getDataverseName();
                     }
-                    IAType paramType = metadataProvider.findType(paramTypeDataverseName, paramTypeName);
-                    if (paramType == null) {
+                    Datatype paramTypeEntity = metadataProvider.findTypeEntity(paramTypeDataverseName, paramTypeName);
+                    if (paramTypeEntity == null || paramTypeEntity.getIsAnonymous()) {
                         throw new CompilationException(ErrorCode.UNKNOWN_TYPE, sourceLoc, paramTypeName);
                     }
-                    resultType = dependentType = new TypeSignature(paramTypeDataverseName, paramTypeName);
+                    paramTypeSignature = depTypeSignature = new TypeSignature(paramTypeDataverseName, paramTypeName);
                 }
                 break;
             case ORDEREDLIST:
@@ -2153,25 +2214,24 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 paramTypeName = TypeUtil.createFunctionParameterTypeName(functionSignature.getName(),
                         functionSignature.getArity(), paramIdx);
                 IAType paramType = translateType(paramTypeDataverseName, paramTypeName, paramTypeExpr, mdTxnCtx);
-                MetadataManager.INSTANCE.addDatatype(mdTxnCtx,
-                        new Datatype(paramTypeDataverseName, paramTypeName, paramType, true));
-                resultType = new TypeSignature(paramTypeDataverseName, paramTypeName);
-                dependentType = FunctionUtil.getTypeDependencyFromFunctionParameter(paramTypeExpr,
-                        functionSignature.getDataverseName());
+                paramTypeSignature = new TypeSignature(paramTypeDataverseName, paramTypeName);
+                depTypeSignature =
+                        FunctionUtil.getTypeDependencyFromFunctionParameter(paramTypeExpr, paramTypeDataverseName);
+                paramInlineTypeEntity = new Datatype(paramTypeDataverseName, paramTypeName, paramType, true);
                 break;
             default:
                 throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc);
         }
 
-        return new Pair<>(resultType, dependentType);
+        return new Triple<>(paramTypeSignature, depTypeSignature, paramInlineTypeEntity);
     }
 
     protected void handleFunctionDropStatement(MetadataProvider metadataProvider, Statement stmt) throws Exception {
         FunctionDropStatement stmtDropFunction = (FunctionDropStatement) stmt;
         FunctionSignature signature = stmtDropFunction.getFunctionSignature();
-        signature.setDataverseName(getActiveDataverseName(signature.getDataverseName()));
-        lockUtil.dropFunctionBegin(lockManager, metadataProvider.getLocks(), signature.getDataverseName(),
-                signature.getName());
+        DataverseName dataverseName = getActiveDataverseName(signature.getDataverseName());
+        signature.setDataverseName(dataverseName);
+        lockUtil.dropFunctionBegin(lockManager, metadataProvider.getLocks(), dataverseName, signature.getName());
         try {
             doDropFunction(metadataProvider, stmtDropFunction, signature);
         } finally {
@@ -2181,18 +2241,18 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
 
     protected boolean doDropFunction(MetadataProvider metadataProvider, FunctionDropStatement stmtDropFunction,
             FunctionSignature signature) throws Exception {
+        DataverseName dataverseName = signature.getDataverseName();
         SourceLocation sourceLoc = stmtDropFunction.getSourceLocation();
         MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
         metadataProvider.setMetadataTxnContext(mdTxnCtx);
         try {
-            Dataverse dataverse = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, signature.getDataverseName());
+            Dataverse dataverse = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverseName);
             if (dataverse == null) {
                 if (stmtDropFunction.getIfExists()) {
                     MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
                     return false;
                 } else {
-                    throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc,
-                            signature.getDataverseName());
+                    throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverseName);
                 }
             }
             Function function = MetadataManager.INSTANCE.getFunction(mdTxnCtx, signature);
@@ -2201,11 +2261,16 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                     MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
                     return false;
                 } else {
-                    throw new CompilationException(ErrorCode.UNKNOWN_FUNCTION, sourceLoc, signature.toString(false));
+                    throw new CompilationException(ErrorCode.UNKNOWN_FUNCTION, sourceLoc, signature.toString());
                 }
             }
 
+            List<TypeSignature> inlineTypes = TypeUtil.getFunctionInlineTypes(function);
+
             MetadataManager.INSTANCE.dropFunction(mdTxnCtx, signature);
+            for (TypeSignature inlineType : inlineTypes) {
+                MetadataManager.INSTANCE.dropDatatype(mdTxnCtx, inlineType.getDataverseName(), inlineType.getName());
+            }
             MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
             return true;
         } catch (Exception e) {
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/NullMissingTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/NullMissingTest.java
index 3b4f78f..55a1a80 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/NullMissingTest.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/NullMissingTest.java
@@ -31,7 +31,6 @@ import java.util.Set;
 
 import org.apache.asterix.common.annotations.MissingNullInOutFunction;
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.FunctionInfo;
 import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.om.types.ATypeTag;
@@ -40,6 +39,7 @@ import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
 import org.apache.asterix.runtime.functions.FunctionCollection;
 import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
@@ -68,7 +68,7 @@ public class NullMissingTest {
     private static final Logger LOGGER = LogManager.getLogger();
 
     // those are the functions that need IATypes of their args and use them in the function constructor
-    private static Set<FunctionInfo> functionsRequiringTypes = new HashSet<>();
+    private static Set<FunctionIdentifier> functionsRequiringTypes = new HashSet<>();
 
     @Parameters(name = "NullMissingTest {index}: {0}")
     public static Collection<Object[]> tests() {
@@ -132,7 +132,7 @@ public class NullMissingTest {
             Pair<IScalarEvaluatorFactory[], IAType[]> argumentsAndTypesPair = argEvalFactoryIterator.next();
 
             // Set the IAType if it's needed
-            if (functionsRequiringTypes.contains(new FunctionInfo(funcDesc.getIdentifier(), true))) {
+            if (functionsRequiringTypes.contains(funcDesc.getIdentifier())) {
                 funcDesc.setImmutableStates((Object[]) argumentsAndTypesPair.second);
             }
 
@@ -193,21 +193,21 @@ public class NullMissingTest {
     @BeforeClass
     public static void buildFunctionsRequiringType() {
         // Those are the functions that need IATypes of their args and use them in the function constructor
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.ARRAY_POSITION, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.ARRAY_CONTAINS, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.ARRAY_SORT, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.ARRAY_DISTINCT, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.ARRAY_EXCEPT, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.EQ, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.LT, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.GT, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.GE, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.LE, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.NEQ, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.MISSING_IF, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.NAN_IF, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.NEGINF_IF, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.NULL_IF, true));
-        functionsRequiringTypes.add(new FunctionInfo(BuiltinFunctions.POSINF_IF, true));
+        functionsRequiringTypes.add(BuiltinFunctions.ARRAY_POSITION);
+        functionsRequiringTypes.add(BuiltinFunctions.ARRAY_CONTAINS);
+        functionsRequiringTypes.add(BuiltinFunctions.ARRAY_SORT);
+        functionsRequiringTypes.add(BuiltinFunctions.ARRAY_DISTINCT);
+        functionsRequiringTypes.add(BuiltinFunctions.ARRAY_EXCEPT);
+        functionsRequiringTypes.add(BuiltinFunctions.EQ);
+        functionsRequiringTypes.add(BuiltinFunctions.LT);
+        functionsRequiringTypes.add(BuiltinFunctions.GT);
+        functionsRequiringTypes.add(BuiltinFunctions.GE);
+        functionsRequiringTypes.add(BuiltinFunctions.LE);
+        functionsRequiringTypes.add(BuiltinFunctions.NEQ);
+        functionsRequiringTypes.add(BuiltinFunctions.MISSING_IF);
+        functionsRequiringTypes.add(BuiltinFunctions.NAN_IF);
+        functionsRequiringTypes.add(BuiltinFunctions.NEGINF_IF);
+        functionsRequiringTypes.add(BuiltinFunctions.NULL_IF);
+        functionsRequiringTypes.add(BuiltinFunctions.POSINF_IF);
     }
 }
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
index 52d7d19..8cf7085 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
@@ -17,7 +17,7 @@
  * under the License.
  */
 /*
- * Description  : Create functions and drop that function and query metadata 
+ * Description  : Create functions and drop that function and query metadata
  *              : to verify entries in Function dataset for the dropped UDF.
  * Expected Res : Success
  * Date         : Sep 17 2012
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/results/basic/meta06/meta06.1.adm b/asterixdb/asterix-app/src/test/resources/metadata/results/basic/meta06/meta06.1.adm
index 9df885d..a7ebb36 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/results/basic/meta06/meta06.1.adm
+++ b/asterixdb/asterix-app/src/test/resources/metadata/results/basic/meta06/meta06.1.adm
@@ -1 +1 @@
-{ "DataverseName": "testdv", "Name": "fun01", "Arity": "0", "Params": [  ], "ReturnType": "any", "Definition": "\"This is an AQL Bodied UDF\"", "Language": "AQL", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [  ] }
\ No newline at end of file
+{ "DataverseName": "testdv", "Name": "fun01", "Arity": "0", "Params": [  ], "ReturnType": "", "Definition": "\"This is an AQL Bodied UDF\"", "Language": "AQL", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ] }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.1.ddl.sqlpp
similarity index 91%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.1.ddl.sqlpp
index 14a68c0..76cc70d 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.1.ddl.sqlpp
@@ -16,5 +16,5 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-use test;
-drop function test@0 if exists;
\ No newline at end of file
+DROP DATAVERSE externallibtest if exists;
+CREATE DATAVERSE  externallibtest;
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.10.query.sqlpp
similarity index 66%
copy from asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.10.query.sqlpp
index 52d7d19..58202fb 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.10.query.sqlpp
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description  : Create functions and drop that function and query metadata 
- *              : to verify entries in Function dataset for the dropped UDF.
- * Expected Res : Success
- * Date         : Sep 17 2012
- */
 
-drop dataverse test if exists;
-create dataverse test;
-
-create function test.foo(){
-"drop this function"
+select value {
+  "type": "eval",
+  "value": externallibtest.f2(3, 4)
 }
-
-drop function test.foo@0;
-
+union all
+select value {
+  "type": "meta-functions",
+  "value": ( select value m from Metadata.`Function` m where DataverseName = "externallibtest" )
+}
+union all
+select value {
+  "type": "meta-types",
+  "value": ( select value object_remove(m, "Timestamp") from Metadata.`Datatype` m where DataverseName = "externallibtest" )
+}
+order by `type`;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.11.ddl.sqlpp
similarity index 85%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.11.ddl.sqlpp
index c0ee51a..66e94b1 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.11.ddl.sqlpp
@@ -16,6 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-use experiments;
-drop function test_func0@1;
 
+/*
+ * Test CREATE OR REPLACE FUNCTION.
+ * Replace with SQL++ function
+ */
+
+create or replace function externallibtest.f2(a, b) {
+  a * b
+};
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.12.query.sqlpp
similarity index 66%
copy from asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.12.query.sqlpp
index 52d7d19..46f8b50 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.12.query.sqlpp
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description  : Create functions and drop that function and query metadata 
- *              : to verify entries in Function dataset for the dropped UDF.
- * Expected Res : Success
- * Date         : Sep 17 2012
- */
 
-drop dataverse test if exists;
-create dataverse test;
-
-create function test.foo(){
-"drop this function"
+select value {
+  "type": "eval",
+  "value": externallibtest.f2(5, 6)
 }
-
-drop function test.foo@0;
-
+union all
+select value {
+  "type": "meta-functions",
+  "value": ( select value m from Metadata.`Function` m where DataverseName = "externallibtest" )
+}
+union all
+select value {
+  "type": "meta-types",
+  "value": ( select value object_remove(m, "Timestamp") from Metadata.`Datatype` m where DataverseName = "externallibtest" )
+}
+order by `type`;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.2.lib.sqlpp
similarity index 88%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.2.lib.sqlpp
index 14a68c0..3dc6eb6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.2.lib.sqlpp
@@ -16,5 +16,4 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-use test;
-drop function test@0 if exists;
\ No newline at end of file
+install externallibtest testlib admin admin target/data/externallib/asterix-external-data-testlib.zip
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.3.ddl.sqlpp
similarity index 82%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.3.ddl.sqlpp
index c0ee51a..a16d594 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.3.ddl.sqlpp
@@ -16,6 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-use experiments;
-drop function test_func0@1;
 
+/*
+ * Test default dataverse for the library reference
+ */
+
+create function externallibtest.f(a)
+  as "org.apache.asterix.external.library.OpenCapitalFinderFactory" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.4.query.sqlpp
similarity index 65%
copy from asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.4.query.sqlpp
index 52d7d19..10772ec 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.4.query.sqlpp
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description  : Create functions and drop that function and query metadata 
- *              : to verify entries in Function dataset for the dropped UDF.
- * Expected Res : Success
- * Date         : Sep 17 2012
- */
 
-drop dataverse test if exists;
-create dataverse test;
-
-create function test.foo(){
-"drop this function"
+select value {
+  "type": "eval",
+  "value": externallibtest.f("United States")
 }
-
-drop function test.foo@0;
-
+union all
+select value {
+  "type": "meta-functions",
+  "value": ( select value m from Metadata.`Function` m where DataverseName = "externallibtest" )
+}
+union all
+select value {
+  "type": "meta-types",
+  "value": ( select value object_remove(m, "Timestamp") from Metadata.`Datatype` m where DataverseName = "externallibtest" )
+}
+order by `type`;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.5.ddl.sqlpp
similarity index 77%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.5.ddl.sqlpp
index 8cb4580..2727090 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.5.ddl.sqlpp
@@ -16,11 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 /*
- * Description  : This test case is to verify the fix for issue 455
-                 : https://code.google.com/p/asterixdb/issues/detail?id=455
- * Expected Res : Failure
- * Date         : 19th May 2013
+ * Test CREATE OR REPLACE FUNCTION.
+ *
+ * Replace implementation
+ * Replace parameter type.
  */
 
-drop function test.printName@0;
+create or replace function externallibtest.f(a: [int32]) returns int32
+  as "org.apache.asterix.external.library.MyArraySumFactory" at testlib;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.6.query.sqlpp
similarity index 66%
copy from asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.6.query.sqlpp
index 52d7d19..34e8c26 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.6.query.sqlpp
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description  : Create functions and drop that function and query metadata 
- *              : to verify entries in Function dataset for the dropped UDF.
- * Expected Res : Success
- * Date         : Sep 17 2012
- */
 
-drop dataverse test if exists;
-create dataverse test;
-
-create function test.foo(){
-"drop this function"
+select value {
+  "type": "eval",
+  "value": externallibtest.f([1, 2])
 }
-
-drop function test.foo@0;
-
+union all
+select value {
+  "type": "meta-functions",
+  "value": ( select value m from Metadata.`Function` m where DataverseName = "externallibtest" )
+}
+union all
+select value {
+  "type": "meta-types",
+  "value": ( select value object_remove(m, "Timestamp") from Metadata.`Datatype` m where DataverseName = "externallibtest" )
+}
+order by `type`;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.7.ddl.sqlpp
similarity index 75%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.7.ddl.sqlpp
index 8cb4580..9276797 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.7.ddl.sqlpp
@@ -16,11 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 /*
- * Description  : This test case is to verify the fix for issue 455
-                 : https://code.google.com/p/asterixdb/issues/detail?id=455
- * Expected Res : Failure
- * Date         : 19th May 2013
+ * Test CREATE OR REPLACE FUNCTION.
+ *
+ * Replace implementation
+ * Replace parameter type with 'any'
+ * Replace return type with 'any'
  */
 
-drop function test.printName@0;
+create or replace function externallibtest.f(a)
+ as "org.apache.asterix.external.library.OpenCapitalFinderFactory" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.8.query.sqlpp
similarity index 66%
copy from asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.8.query.sqlpp
index 52d7d19..884c63d 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.8.query.sqlpp
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description  : Create functions and drop that function and query metadata 
- *              : to verify entries in Function dataset for the dropped UDF.
- * Expected Res : Success
- * Date         : Sep 17 2012
- */
 
-drop dataverse test if exists;
-create dataverse test;
-
-create function test.foo(){
-"drop this function"
+select value {
+  "type": "eval",
+  "value": externallibtest.f("Italy")
 }
-
-drop function test.foo@0;
-
+union all
+select value {
+  "type": "meta-functions",
+  "value": ( select value m from Metadata.`Function` m where DataverseName = "externallibtest" )
+}
+union all
+select value {
+  "type": "meta-types",
+  "value": ( select value object_remove(m, "Timestamp") from Metadata.`Datatype` m where DataverseName = "externallibtest" )
+}
+order by `type`;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.9.ddl.sqlpp
similarity index 78%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.9.ddl.sqlpp
index f8e2ae7..bd8d726 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/create-or-replace-function-1/create-or-replace-function-1.9.ddl.sqlpp
@@ -16,6 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
- USE externallibtest;
 
-drop function externallibtest.mysum@2;
+/*
+ * Test CREATE OR REPLACE FUNCTION.
+ *
+ * Create function with 2 parameters
+ */
+
+create function externallibtest.f2(a: int32, b: int32) returns int32
+  as "org.apache.asterix.external.library.MySumFactory" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp
index f8e2ae7..58447ec 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.5.ddl.sqlpp
@@ -18,4 +18,4 @@
  */
  USE externallibtest;
 
-drop function externallibtest.mysum@2;
+drop function externallibtest.mysum(a: int32, b: int32);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
index c0ee51a..a92d8b7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
@@ -17,5 +17,4 @@
  * under the License.
  */
 use experiments;
-drop function test_func0@1;
-
+drop function test_func0@1;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
index c0ee51a..57d0a78 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
@@ -17,5 +17,5 @@
  * under the License.
  */
 use experiments;
-drop function test_func0@1;
+drop function test_func0(x);
 
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
index 14a68c0..e39a9fc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
@@ -17,4 +17,4 @@
  * under the License.
  */
 use test;
-drop function test@0 if exists;
\ No newline at end of file
+drop function test() if exists;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.3.ddl.sqlpp
index 14a68c0..e39a9fc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.3.ddl.sqlpp
@@ -17,4 +17,4 @@
  * under the License.
  */
 use test;
-drop function test@0 if exists;
\ No newline at end of file
+drop function test() if exists;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/default/default.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/default/default.3.ddl.sqlpp
index a0bc671..82bcc32 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/default/default.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/default/default.3.ddl.sqlpp
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-DROP function foo@0;
+DROP function foo();
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.1.ddl.sqlpp
similarity index 77%
copy from asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.1.ddl.sqlpp
index 52d7d19..c111196 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.1.ddl.sqlpp
@@ -16,19 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 /*
- * Description  : Create functions and drop that function and query metadata 
- *              : to verify entries in Function dataset for the dropped UDF.
+ * Description  : Test CREATE OR REPLACE FUNCTION
  * Expected Res : Success
- * Date         : Sep 17 2012
  */
 
 drop dataverse test if exists;
 create dataverse test;
+use test;
 
-create function test.foo(){
-"drop this function"
-}
+create function f1(a, b) {
+  a + b
+};
 
-drop function test.foo@0;
+/* Replace an existing function */
+create or replace function f1(a, b) {
+  a - b
+};
 
+/* Create new function */
+create or replace function f2(a, b) {
+  a * b
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.2.query.sqlpp
similarity index 95%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.2.query.sqlpp
index 14a68c0..8e6c136 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.2.query.sqlpp
@@ -16,5 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 use test;
-drop function test@0 if exists;
\ No newline at end of file
+
+{
+  "f1": f1(5, 2),
+  "f2": f2(5, 2)
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.3.query.sqlpp
similarity index 91%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.3.query.sqlpp
index 14a68c0..7a19fc3 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/drop_if_exists/drop_if_exists.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.3.query.sqlpp
@@ -16,5 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 use test;
-drop function test@0 if exists;
\ No newline at end of file
+
+select f.Name, f.`Definition`
+from Metadata.`Function` f
+order by f.Name;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp
index 7e7b60e..8e09034 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp
@@ -39,4 +39,4 @@ create function f0(message, text){
 };
 
 use C;
-drop function f1@2;
\ No newline at end of file
+drop function f1(message, text);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp
index 3a7f4dc..f301fc6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp
@@ -35,4 +35,4 @@ create function f0(message, text){
   C.f1(message,text)
 };
 
-drop function f1@2;
\ No newline at end of file
+drop function f1(message, text);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.1.ddl.sqlpp
similarity index 81%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.1.ddl.sqlpp
index c0ee51a..be5e07d 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.1.ddl.sqlpp
@@ -16,6 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+/*
+ * Description  : Test drop function
+ * Expected Res : Success
+ */
+
+drop dataverse experiments if exists;
+create dataverse experiments;
 use experiments;
-drop function test_func0@1;
 
+create function my_sum(a, b) {
+  a + b
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.2.query.sqlpp
similarity index 96%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.2.query.sqlpp
index c0ee51a..0f0de1a 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.2.query.sqlpp
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 use experiments;
-drop function test_func0@1;
 
+my_sum(2, 3);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/default/default.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.3.ddl.sqlpp
similarity index 95%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/default/default.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.3.ddl.sqlpp
index a0bc671..7023e16 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/default/default.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.3.ddl.sqlpp
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-DROP function foo@0;
+drop function experiments.my_sum(a, b);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.4.query.sqlpp
similarity index 90%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.4.query.sqlpp
index c0ee51a..a998ac1 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-function-1/drop-function-1.4.query.sqlpp
@@ -16,6 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+/*
+ * Description  : Error. Unknown function
+ * Expected Res : Failure
+ */
+
 use experiments;
-drop function test_func0@1;
 
+my_sum(2, 3);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp
index 8cb4580..d293b8e 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/query-issue455/query-issue455.4.ddl.sqlpp
@@ -23,4 +23,4 @@
  * Date         : 19th May 2013
  */
 
-drop function test.printName@0;
+drop function test.printName();
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/udf33_overloading/udf33_overloading.1.ddl.sqlpp
similarity index 78%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/udf33_overloading/udf33_overloading.1.ddl.sqlpp
index c0ee51a..670922d 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-no-longer-used-by-feed/drop-function-no-longer-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/udf33_overloading/udf33_overloading.1.ddl.sqlpp
@@ -16,6 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+/*
+ * Description  : Function overloading is allowed
+ * Expected Res : Success
+ */
+
+drop dataverse experiments if exists;
+create dataverse experiments;
 use experiments;
-drop function test_func0@1;
 
+create function myfn(a) {
+  a
+};
+
+create function myfn(a, b) {
+  a + b
+};
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/udf33_overloading/udf33_overloading.2.query.sqlpp
similarity index 86%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/udf33_overloading/udf33_overloading.2.query.sqlpp
index c0ee51a..c9359fc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/drop-function-used-by-feed/drop-function-used-by-feed.3.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/udf33_overloading/udf33_overloading.2.query.sqlpp
@@ -16,6 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+/*
+ * Description  : Function overloading is allowed
+ * Expected Res : Success
+ */
+
 use experiments;
-drop function test_func0@1;
 
+{
+  "myfn_1": myfn(2),
+  "myfn_2": myfn(2, 3)
+}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv15/cross-dv15.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv15/cross-dv15.1.adm
index ed83617..24d5071 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv15/cross-dv15.1.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv15/cross-dv15.1.adm
@@ -1,3 +1,3 @@
-{ "DataverseName": "testdv1", "Name": "fun01", "Arity": "0", "ReturnType": "any" }
-{ "DataverseName": "testdv1", "Name": "fun02", "Arity": "1", "ReturnType": "any" }
-{ "DataverseName": "testdv1", "Name": "fun03", "Arity": "2", "ReturnType": "any" }
+{ "DataverseName": "testdv1", "Name": "fun01", "Arity": "0", "ReturnType": "" }
+{ "DataverseName": "testdv1", "Name": "fun02", "Arity": "1", "ReturnType": "" }
+{ "DataverseName": "testdv1", "Name": "fun03", "Arity": "2", "ReturnType": "" }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.10.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.10.adm
new file mode 100644
index 0000000..c0c9e29
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.10.adm
@@ -0,0 +1,3 @@
+{ "type": "eval", "value": 7 }
+{ "type": "meta-functions", "value": [ { "DataverseName": "externallibtest", "Name": "f", "Arity": "1", "Params": [ "a" ], "ReturnType": "", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ null ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.OpenCapitalFinderFactory" ], "NullCall": false, "Deterministic": true }, { "DataverseName": "externalli [...]
+{ "type": "meta-types", "value": [  ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.12.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.12.adm
new file mode 100644
index 0000000..7775988
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.12.adm
@@ -0,0 +1,3 @@
+{ "type": "eval", "value": 30 }
+{ "type": "meta-functions", "value": [ { "DataverseName": "externallibtest", "Name": "f", "Arity": "1", "Params": [ "a" ], "ReturnType": "", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ null ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.OpenCapitalFinderFactory" ], "NullCall": false, "Deterministic": true }, { "DataverseName": "externalli [...]
+{ "type": "meta-types", "value": [  ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.4.adm
new file mode 100644
index 0000000..968097c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.4.adm
@@ -0,0 +1,3 @@
+{ "type": "eval", "value": { "country": "United States", "capital": "Washington D.C." } }
+{ "type": "meta-functions", "value": [ { "DataverseName": "externallibtest", "Name": "f", "Arity": "1", "Params": [ "a" ], "ReturnType": "", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ null ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.OpenCapitalFinderFactory" ], "NullCall": false, "Deterministic": true } ] }
+{ "type": "meta-types", "value": [  ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.6.adm
new file mode 100644
index 0000000..590b8de
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.6.adm
@@ -0,0 +1,3 @@
+{ "type": "eval", "value": 3 }
+{ "type": "meta-functions", "value": [ { "DataverseName": "externallibtest", "Name": "f", "Arity": "1", "Params": [ "a" ], "ReturnType": "int32", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "$f$t$f$1$0" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.MyArraySumFactory" ], "NullCall": false, "Deterministic": true } ] }
+{ "type": "meta-types", "value": [ { "DataverseName": "externallibtest", "DatatypeName": "$f$t$f$1$0", "Derived": { "Tag": "ORDEREDLIST", "IsAnonymous": true, "OrderedList": "int32" } } ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.8.adm
new file mode 100644
index 0000000..4fda008
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/create-or-replace-function-1/create-or-replace-function-1.8.adm
@@ -0,0 +1,3 @@
+{ "type": "eval", "value": { "country": "Italy", "capital": "Rome" } }
+{ "type": "meta-functions", "value": [ { "DataverseName": "externallibtest", "Name": "f", "Arity": "1", "Params": [ "a" ], "ReturnType": "", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ null ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.OpenCapitalFinderFactory" ], "NullCall": false, "Deterministic": true } ] }
+{ "type": "meta-types", "value": [  ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/udf_metadata/udf_metadata.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/udf_metadata/udf_metadata.3.adm
index 51254a5..6d0ea72 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/udf_metadata/udf_metadata.3.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/udf_metadata/udf_metadata.3.adm
@@ -1,5 +1,5 @@
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn001", "Arity": "0", "Params": [  ], "ReturnType": "any", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [  ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn002", "Arity": "1", "Params": [ "a" ], "ReturnType": "any", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "any" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
+{ "fn": { "DataverseName": "externallibtest", "Name": "myfn001", "Arity": "0", "Params": [  ], "ReturnType": "", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [  ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
+{ "fn": { "DataverseName": "externallibtest", "Name": "myfn002", "Arity": "1", "Params": [ "a" ], "ReturnType": "", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ null ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
 { "fn": { "DataverseName": "externallibtest", "Name": "myfn003", "Arity": "3", "Params": [ "a", "b", "c" ], "ReturnType": "string", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "string" }, { "Type": "$f$t$myfn003$3$1" }, { "Type": "$f$t$myfn003$3$2" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCa [...]
 { "fn": { "DataverseName": "externallibtest", "Name": "myfn004", "Arity": "2", "Params": [ "a", "b" ], "ReturnType": "CountryCapitalType", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "CountryCapitalType" }, { "Type": "$f$t$myfn004$2$1" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.Ca [...]
 { "fn": { "DataverseName": "externallibtest", "Name": "myfn005", "Arity": "4", "Params": [ "a", "b", "c", "d" ], "ReturnType": "string", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "int16" }, { "Type": "$f$t$myfn005$4$1" }, { "Type": "CountryCapitalType" }, { "Type": "$f$t$myfn005$4$3" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIde [...]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.2.adm
new file mode 100644
index 0000000..e57f576
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.2.adm
@@ -0,0 +1 @@
+{ "f1": 3, "f2": 10 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.3.adm
new file mode 100644
index 0000000..63617d3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/create-or-replace-function-1/create-or-replace-function-1.3.adm
@@ -0,0 +1,2 @@
+{ "Name": "f1", "Definition": "a - b" }
+{ "Name": "f2", "Definition": "a * b" }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/drop-function-1/drop-function-1.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/drop-function-1/drop-function-1.2.adm
new file mode 100644
index 0000000..7813681
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/drop-function-1/drop-function-1.2.adm
@@ -0,0 +1 @@
+5
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/single-line-definition/single-line-definition.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/single-line-definition/single-line-definition.1.adm
index 10c6384..abd3c72 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/single-line-definition/single-line-definition.1.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/single-line-definition/single-line-definition.1.adm
@@ -1 +1 @@
-{ "DataverseName": "test", "Name": "printName", "Arity": "0", "ReturnType": "any", "Definition": "'AsterixDB Shared nothing parallel BDMS'" }
\ No newline at end of file
+{ "DataverseName": "test", "Name": "printName", "Arity": "0", "ReturnType": "", "Definition": "'AsterixDB Shared nothing parallel BDMS'" }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf28/udf28.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf28/udf28.1.adm
index 07e4942..eb1d4d6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf28/udf28.1.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf28/udf28.1.adm
@@ -1 +1 @@
-{ "DataverseName": "test", "Name": "f1", "Arity": "0", "ReturnType": "any", "Definition": "100" }
\ No newline at end of file
+{ "DataverseName": "test", "Name": "f1", "Arity": "0", "ReturnType": "", "Definition": "100" }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf32_metadata/udf32_metadata.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf32_metadata/udf32_metadata.2.adm
index 07f3289..7cf89ce 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf32_metadata/udf32_metadata.2.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf32_metadata/udf32_metadata.2.adm
@@ -1,3 +1,3 @@
-{ "fn": { "DataverseName": "test", "Name": "myfn001", "Arity": "0", "Params": [  ], "ReturnType": "any", "Definition": "42", "Language": "SQLPP", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [  ] } }
-{ "fn": { "DataverseName": "test", "Name": "myfn002", "Arity": "1", "Params": [ "a" ], "ReturnType": "any", "Definition": "a", "Language": "SQLPP", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "any" } ] } }
-{ "fn": { "DataverseName": "test", "Name": "myfn003", "Arity": "2", "Params": [ "a", "b" ], "ReturnType": "any", "Definition": "a + b", "Language": "SQLPP", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "any" }, { "Type": "any" } ] } }
+{ "fn": { "DataverseName": "test", "Name": "myfn001", "Arity": "0", "Params": [  ], "ReturnType": "", "Definition": "42", "Language": "SQLPP", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ] } }
+{ "fn": { "DataverseName": "test", "Name": "myfn002", "Arity": "1", "Params": [ "a" ], "ReturnType": "", "Definition": "a", "Language": "SQLPP", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ] } }
+{ "fn": { "DataverseName": "test", "Name": "myfn003", "Arity": "2", "Params": [ "a", "b" ], "ReturnType": "", "Definition": "a + b", "Language": "SQLPP", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ] } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf33_overloading/udf33_overloading.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf33_overloading/udf33_overloading.2.adm
new file mode 100644
index 0000000..8024624
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/udf33_overloading/udf33_overloading.2.adm
@@ -0,0 +1 @@
+{ "myfn_1": 2, "myfn_2": 5 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_01/closed-record-constructor_01.3.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_01/closed-record-constructor_01.3.ast
index de7eb1a..bfe64db 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_01/closed-record-constructor_01.3.ast
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_01/closed-record-constructor_01.3.ast
@@ -2,7 +2,7 @@ DataverseUse test
 Set import-private-functions=true
 Query:
 SELECT ELEMENT [
-FunctionCall asterix.closed-object-constructor@8[
+FunctionCall asterix.closed-object-constructor[
   LiteralExpr [STRING] [foo1]
   LiteralExpr [LONG] [10]
   LiteralExpr [STRING] [bar1]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_02/closed-record-constructor_02.3.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_02/closed-record-constructor_02.3.ast
index f38fc1e..d158af6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_02/closed-record-constructor_02.3.ast
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/closed-object-constructor_02/closed-record-constructor_02.3.ast
@@ -2,11 +2,11 @@ DataverseUse test
 Set import-private-functions=true
 Query:
 SELECT ELEMENT [
-FunctionCall asterix.closed-object-constructor@8[
+FunctionCall asterix.closed-object-constructor[
   LiteralExpr [STRING] [foo1]
   LiteralExpr [LONG] [10]
   LiteralExpr [STRING] [bar1]
-  FunctionCall asterix.closed-object-constructor@12[
+  FunctionCall asterix.closed-object-constructor[
     LiteralExpr [STRING] [bar1.1]
     LiteralExpr [LONG] [10]
     LiteralExpr [STRING] [bar1.2]
@@ -14,7 +14,7 @@ FunctionCall asterix.closed-object-constructor@8[
     LiteralExpr [STRING] [bar1.3]
     LiteralExpr [LONG] [30]
     LiteralExpr [STRING] [bar1.4]
-    FunctionCall asterix.closed-object-constructor@8[
+    FunctionCall asterix.closed-object-constructor[
       LiteralExpr [STRING] [bar1.4.1]
       LiteralExpr [LONG] [10]
       LiteralExpr [STRING] [bar1.4.2]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_01/open-record-constructor_01.3.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_01/open-record-constructor_01.3.ast
index a94db4a..6dbabcb 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_01/open-record-constructor_01.3.ast
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_01/open-record-constructor_01.3.ast
@@ -2,7 +2,7 @@ DataverseUse test
 Set import-private-functions=true
 Query:
 SELECT ELEMENT [
-FunctionCall asterix.open-object-constructor@8[
+FunctionCall asterix.open-object-constructor[
   LiteralExpr [STRING] [foo1]
   LiteralExpr [LONG] [10]
   LiteralExpr [STRING] [bar1]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_02/open-record-constructor_02.3.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_02/open-record-constructor_02.3.ast
index 0599ad9..c30e88f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_02/open-record-constructor_02.3.ast
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/objects/open-object-constructor_02/open-record-constructor_02.3.ast
@@ -2,11 +2,11 @@ DataverseUse test
 Set import-private-functions=true
 Query:
 SELECT ELEMENT [
-FunctionCall asterix.open-object-constructor@8[
+FunctionCall asterix.open-object-constructor[
   LiteralExpr [STRING] [foo1]
   LiteralExpr [LONG] [10]
   LiteralExpr [STRING] [bar1]
-  FunctionCall asterix.closed-object-constructor@12[
+  FunctionCall asterix.closed-object-constructor[
     LiteralExpr [STRING] [bar1.1]
     LiteralExpr [LONG] [10]
     LiteralExpr [STRING] [bar1.2]
@@ -14,7 +14,7 @@ FunctionCall asterix.open-object-constructor@8[
     LiteralExpr [STRING] [bar1.3]
     LiteralExpr [LONG] [30]
     LiteralExpr [STRING] [bar1.4]
-    FunctionCall asterix.closed-object-constructor@8[
+    FunctionCall asterix.closed-object-constructor[
       LiteralExpr [STRING] [bar1.4.1]
       LiteralExpr [LONG] [10]
       LiteralExpr [STRING] [bar1.4.2]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/window/misc_01/misc_01.3.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/window/misc_01/misc_01.3.ast
index d5f1993..86cd5b1 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/window/misc_01/misc_01.3.ast
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/window/misc_01/misc_01.3.ast
@@ -116,7 +116,7 @@ Let Variable [ Name=$rank_result_expected ]
   ]
 Let Variable [ Name=$rank_result_actual ]
   :=
-  WINDOW asterix.rank-impl@-1[
+  WINDOW asterix.rank-impl[
   ]
     AS Variable [ Name=#2 ]
     (
@@ -165,7 +165,7 @@ Let Variable [ Name=$percent_rank_result_expected ]
   ]
 Let Variable [ Name=$percent_rank_result_actual ]
   :=
-  WINDOW asterix.percent-rank-impl@-1[
+  WINDOW asterix.percent-rank-impl[
   ]
     AS Variable [ Name=#3 ]
     (
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it_sqlpp.xml
index f5b4ede..ea5d279 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it_sqlpp.xml
@@ -24,6 +24,11 @@
 
   <test-group name="external-library">
     <test-case FilePath="external-library">
+      <compilation-unit name="create-or-replace-function-1">
+        <output-dir compare="Text">create-or-replace-function-1</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="external-library">
       <compilation-unit name="type_validation">
         <output-dir compare="Text">type_validation</output-dir>
       </compilation-unit>
@@ -46,7 +51,7 @@
     <test-case FilePath="external-library">
       <compilation-unit name="mysum_dropinuse">
         <output-dir compare="Text">mysum_dropinuse</output-dir>
-        <expected-error>Cannot drop library externallibtest.testlib being used by funciton externallibtest.mysum@2</expected-error>
+        <expected-error>Cannot drop library externallibtest.testlib being used by funciton externallibtest.mysum(2)</expected-error>
       </compilation-unit>
     </test-case>
     <test-case FilePath="external-library">
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 1495ce0..c37010c 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -11764,48 +11764,59 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
+      <compilation-unit name="create-or-replace-function-1">
+        <output-dir compare="Text">create-or-replace-function-1</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="user-defined-functions">
       <compilation-unit name="drop-dependency-1">
         <output-dir compare="Text">drop-dependency-1</output-dir>
-        <expected-error>Cannot drop dataverse. Function B.f0@2 depends on function C.f1@2</expected-error>
+        <expected-error>Cannot drop dataverse. Function B.f0(2) depends on function C.f1(2)</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
       <compilation-unit name="drop-dependency-2">
         <output-dir compare="Text">drop-dependency-2</output-dir>
-        <expected-error>Cannot drop dataverse. Function B.f2@2 depends on dataset C.TweetMessages</expected-error>
+        <expected-error>Cannot drop dataverse. Function B.f2(2) depends on dataset C.TweetMessages</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
       <compilation-unit name="drop-dependency-3">
         <output-dir compare="Text">drop-dependency-3</output-dir>
-        <expected-error>Cannot drop function C.f1@2 being used by function B.f0@2</expected-error>
+        <expected-error>Cannot drop function C.f1(2) being used by function B.f0(2)</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
       <compilation-unit name="drop-dependency-4">
         <output-dir compare="Text">drop-dependency-4</output-dir>
-        <expected-error>Cannot drop dataset C.TweetMessages being used by function B.f2@2</expected-error>
+        <expected-error>Cannot drop dataset C.TweetMessages being used by function B.f2(2)</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
       <compilation-unit name="drop-dependency-5">
         <output-dir compare="Text">drop-dependency-5</output-dir>
-        <expected-error>Cannot drop function C.f1@2 being used by function C.f0@2</expected-error>
+        <expected-error>Cannot drop function C.f1(2) being used by function C.f0(2)</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
       <compilation-unit name="drop-dependency-6">
         <output-dir compare="Text">drop-dependency-6</output-dir>
-        <expected-error>Cannot drop dataset C.TweetMessages being used by function C.f2@2</expected-error>
+        <expected-error>Cannot drop dataset C.TweetMessages being used by function C.f2(2)</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
+      <compilation-unit name="drop-function-1">
+        <output-dir compare="Text">drop-function-1</output-dir>
+        <expected-error>ASX1081: Cannot find function with name experiments.my_sum</expected-error>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="user-defined-functions">
       <compilation-unit name="single-line-definition">
         <output-dir compare="Text">single-line-definition</output-dir>
       </compilation-unit>
@@ -12015,6 +12026,11 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
+      <compilation-unit name="udf33_overloading">
+        <output-dir compare="Text">udf33_overloading</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="user-defined-functions">
       <compilation-unit name="f01">
         <output-dir compare="Text">f01</output-dir>
         <expected-error>ASX1081: Cannot find function with name test.tinyint</expected-error>
@@ -12475,7 +12491,7 @@
     <test-case FilePath="feeds">
       <compilation-unit name="drop-function-used-by-feed">
         <output-dir compare="Text">drop-function-used-by-feed</output-dir>
-        <expected-error>Cannot drop function experiments.test_func0@1 being used by feed connection TwitterUsers.UserFeed</expected-error>
+        <expected-error>Cannot drop function experiments.test_func0(1) being used by feed connection TwitterUsers.UserFeed</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
@@ -12487,7 +12503,7 @@
     <test-case FilePath="feeds">
       <compilation-unit name="drop-dataverse-with-function-used-by-feed">
         <output-dir compare="Text">drop-dataverse-with-function-used-by-feed</output-dir>
-        <expected-error>Cannot drop dataverse. Feed connection feeddv.UserFeed depends on function fundv.test_func0@1</expected-error>
+        <expected-error>Cannot drop dataverse. Feed connection feeddv.UserFeed depends on function fundv.test_func0(1)</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/functions/FunctionSignature.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/functions/FunctionSignature.java
index 0771645..1efdd42 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/functions/FunctionSignature.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/functions/FunctionSignature.java
@@ -51,8 +51,12 @@ public class FunctionSignature implements Serializable {
             return false;
         }
         FunctionSignature f = ((FunctionSignature) o);
-        return Objects.equals(dataverseName, f.dataverseName) && name.equals(f.name)
-                && (arity == f.arity || arity == FunctionIdentifier.VARARGS || f.arity == FunctionIdentifier.VARARGS);
+        return Objects.equals(dataverseName, f.dataverseName) && name.equals(f.name) && arity == f.arity;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(dataverseName, name, arity);
     }
 
     @Override
@@ -64,23 +68,29 @@ public class FunctionSignature implements Serializable {
         boolean dataverseNameExists = dataverseName != null;
         String dataverseCanonicalName = dataverseNameExists ? dataverseName.getCanonicalForm() : null;
         int len = (dataverseNameExists ? dataverseCanonicalName.length() + 1 : 0) + name.length()
-                + (includeArity ? 3 : 0);
+                + (includeArity ? 5 : 0);
         StringBuilder sb = new StringBuilder(len);
         if (dataverseNameExists) {
             sb.append(dataverseCanonicalName).append('.');
         }
         sb.append(name);
         if (includeArity) {
-            sb.append('@').append(arity);
+            sb.append('(');
+            switch (arity) {
+                case FunctionIdentifier.VARARGS:
+                    sb.append("...");
+                    break;
+                case 0:
+                    break;
+                default:
+                    sb.append(arity);
+                    break;
+            }
+            sb.append(')');
         }
         return sb.toString();
     }
 
-    @Override
-    public int hashCode() {
-        return Objects.hash(dataverseName, name);
-    }
-
     public DataverseName getDataverseName() {
         return dataverseName;
     }
@@ -109,15 +119,11 @@ public class FunctionSignature implements Serializable {
         return createFunctionIdentifier(dataverseName, name, arity);
     }
 
-    public static FunctionIdentifier createFunctionIdentifier(DataverseName dataverseName, String functionName,
+    private static FunctionIdentifier createFunctionIdentifier(DataverseName dataverseName, String functionName,
             int arity) {
         return new FunctionIdentifier(dataverseName.getCanonicalForm(), functionName, arity);
     }
 
-    public static FunctionIdentifier createFunctionIdentifier(DataverseName dataverseName, String functionName) {
-        return new FunctionIdentifier(dataverseName.getCanonicalForm(), functionName);
-    }
-
     public static DataverseName getDataverseName(FunctionIdentifier fi) {
         String dataverseCanonicalName = fi.getNamespace();
         switch (dataverseCanonicalName) {
diff --git a/asterixdb/asterix-doc/src/main/markdown/sqlpp/5_ddl_function_removal.md b/asterixdb/asterix-doc/src/main/markdown/sqlpp/5_ddl_function_removal.md
index 34b611f..a0742a3 100644
--- a/asterixdb/asterix-doc/src/main/markdown/sqlpp/5_ddl_function_removal.md
+++ b/asterixdb/asterix-doc/src/main/markdown/sqlpp/5_ddl_function_removal.md
@@ -22,7 +22,7 @@
 The CREATE FUNCTION statement creates a **named** function that can then be used and reused in queries.
 The body of a function can be any query expression involving the function's parameters.
 
-    FunctionSpecification ::= "FUNCTION" FunctionOrTypeName IfNotExists ParameterList "{" Expression "}"
+    FunctionSpecification ::= (<OR> <REPLACE>)? "FUNCTION" FunctionOrTypeName IfNotExists ParameterList "{" Expression "}"
 
 The following is an example of a CREATE FUNCTION statement which is similar to our earlier DECLARE FUNCTION example.
 It differs from that example in that it results in a function that is persistently registered by name in the specified dataverse (the current dataverse being used, if not otherwise specified).
@@ -35,9 +35,19 @@ It differs from that example in that it results in a function that is persistent
          WHERE u.id = userId)[0]
      };
 
+The following is an example of CREATE FUNCTION statement that replaces an existing function.
+
+##### Example
+
+    CREATE OR REPLACE FUNCTION friendInfo(userId) {
+        (SELECT u.id, u.name
+         FROM GleambookUsers u
+         WHERE u.id = userId)[0]
+     };
+
 ### <a id="Synonyms"> Synonyms</a>
 
-    SynonymSpecification ::= "SYNONYM" QualifiedName "FOR" QualifiedName IfNotExists
+    SynonymSpecification ::= "SYNONYM" QualifiedName IfNotExists "FOR" QualifiedName 
 
 The CREATE SYNONYM statement creates a synonym for a given dataset.
 This synonym may be used used instead of the dataset name in SELECT, INSERT, UPSERT, DELETE, and LOAD statements.
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlFunctionCallResolverVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlFunctionCallResolverVisitor.java
index 71c26f2..32f368b 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlFunctionCallResolverVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlFunctionCallResolverVisitor.java
@@ -33,7 +33,7 @@ import org.apache.asterix.lang.common.statement.FunctionDecl;
 import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.metadata.declared.MetadataProvider;
 
-public final class AqlFunctionCallResolverVisitor extends AbstractAqlSimpleExpressionVisitor {
+public class AqlFunctionCallResolverVisitor extends AbstractAqlSimpleExpressionVisitor {
 
     private final MetadataProvider metadataProvider;
 
@@ -44,7 +44,7 @@ public final class AqlFunctionCallResolverVisitor extends AbstractAqlSimpleExpre
     public AqlFunctionCallResolverVisitor(MetadataProvider metadataProvider, List<FunctionDecl> declaredFunctions) {
         this.metadataProvider = metadataProvider;
         this.declaredFunctions = FunctionUtil.getFunctionSignatures(declaredFunctions);
-        this.callExprResolver = FunctionUtil.createBuiltinFunctionResolver(metadataProvider);
+        this.callExprResolver = createBuiltinFunctionResolver(metadataProvider);
     }
 
     @Override
@@ -54,4 +54,9 @@ public final class AqlFunctionCallResolverVisitor extends AbstractAqlSimpleExpre
         callExpr.setFunctionSignature(fs);
         return super.visit(callExpr, arg);
     }
+
+    protected BiFunction<String, Integer, FunctionSignature> createBuiltinFunctionResolver(
+            MetadataProvider metadataProvider) {
+        return FunctionUtil.createBuiltinFunctionResolver(metadataProvider);
+    }
 }
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
index b8c3470..e23eec9 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
@@ -50,6 +50,7 @@ import org.apache.asterix.lang.common.expression.RecordConstructor;
 import org.apache.asterix.lang.common.expression.UnaryExpr;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.InsertStatement;
 import org.apache.asterix.lang.common.statement.Query;
 import org.apache.asterix.lang.common.struct.QuantifiedPair;
 
@@ -215,6 +216,17 @@ public class AbstractAqlSimpleExpressionVisitor extends AbstractAqlQueryExpressi
         return null;
     }
 
+    @Override
+    public Expression visit(InsertStatement insertStatement, ILangExpression arg) throws CompilationException {
+        Expression returnExpr = insertStatement.getReturnExpression();
+        if (returnExpr != null) {
+            insertStatement.setReturnExpression(visit(returnExpr, arg));
+        }
+        Query bodyQuery = insertStatement.getQuery();
+        bodyQuery.accept(this, arg);
+        return null;
+    }
+
     protected Expression visit(Expression expr, ILangExpression arg) throws CompilationException {
         return postVisit(preVisit(expr).accept(this, arg));
     }
diff --git a/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj b/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
index 67816c5..35a7a92 100644
--- a/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
+++ b/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
@@ -759,7 +759,7 @@ CreateFunctionStatement FunctionSpecification() throws ParseException:
       for(VarIdentifier v: paramList){
           bodge.add(new Pair<VarIdentifier,TypeExpression>(v,null));
       }
-      return new CreateFunctionStatement(signature, bodge, functionBody, functionBodyExpr, ifNotExists);
+      return new CreateFunctionStatement(signature, bodge, functionBody, functionBodyExpr, false, ifNotExists);
     }
 }
 
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateFunctionStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateFunctionStatement.java
index 2e8a937..baaa2c1 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateFunctionStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateFunctionStatement.java
@@ -52,7 +52,6 @@ public class CreateFunctionStatement extends AbstractStatement {
     private final FunctionSignature signature;
     private final String functionBody;
     private final Expression functionBodyExpression;
-    private final boolean ifNotExists;
     private final List<Pair<VarIdentifier, TypeExpression>> paramList;
     private final TypeExpression returnType;
 
@@ -61,26 +60,29 @@ public class CreateFunctionStatement extends AbstractStatement {
     private final List<String> externalIdentifier;
     private final AdmObjectNode options;
 
+    private final boolean replaceIfExists;
+    private final boolean ifNotExists;
+
     public CreateFunctionStatement(FunctionSignature signature, List<Pair<VarIdentifier, TypeExpression>> paramList,
-            String functionBody, Expression functionBodyExpression, boolean ifNotExists) {
+            String functionBody, Expression functionBodyExpression, boolean replaceIfExists, boolean ifNotExists) {
         this.signature = signature;
         this.functionBody = functionBody;
         this.functionBodyExpression = functionBodyExpression;
-        this.ifNotExists = ifNotExists;
         this.paramList = requireNullTypes(paramList); // parameter type specification is not allowed for inline functions
         this.returnType = null; // return type specification is not allowed for inline functions
         this.libraryDataverseName = null;
         this.libraryName = null;
         this.externalIdentifier = null;
         this.options = null;
+        this.replaceIfExists = replaceIfExists;
+        this.ifNotExists = ifNotExists;
     }
 
     public CreateFunctionStatement(FunctionSignature signature, List<Pair<VarIdentifier, TypeExpression>> paramList,
             TypeExpression returnType, DataverseName libraryDataverseName, String libraryName,
-            List<String> externalIdentifier, RecordConstructor options, boolean ifNotExists)
+            List<String> externalIdentifier, RecordConstructor options, boolean replaceIfExists, boolean ifNotExists)
             throws CompilationException {
         this.signature = signature;
-        this.ifNotExists = ifNotExists;
         this.paramList = paramList;
         this.returnType = returnType;
         this.libraryDataverseName = libraryDataverseName;
@@ -89,6 +91,12 @@ public class CreateFunctionStatement extends AbstractStatement {
         this.options = options == null ? null : ExpressionUtils.toNode(options);
         this.functionBody = null;
         this.functionBodyExpression = null;
+        this.replaceIfExists = replaceIfExists;
+        this.ifNotExists = ifNotExists;
+    }
+
+    public boolean getReplaceIfExists() {
+        return replaceIfExists;
     }
 
     public boolean getIfNotExists() {
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/FunctionDropStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/FunctionDropStatement.java
index 0496ecc..2c59ea9 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/FunctionDropStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/FunctionDropStatement.java
@@ -27,7 +27,7 @@ import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 public class FunctionDropStatement extends AbstractStatement {
 
     private final FunctionSignature signature;
-    private boolean ifExists;
+    private final boolean ifExists;
 
     public FunctionDropStatement(FunctionSignature signature, boolean ifExists) {
         this.signature = signature;
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java
index 60c1c3d..71cd759 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java
@@ -46,6 +46,7 @@ import org.apache.asterix.metadata.declared.MetadataProvider;
 import org.apache.asterix.metadata.entities.BuiltinTypeMap;
 import org.apache.asterix.metadata.entities.Dataverse;
 import org.apache.asterix.metadata.entities.Function;
+import org.apache.asterix.om.functions.BuiltinFunctionInfo;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.TypeSignature;
@@ -55,9 +56,7 @@ import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.common.utils.Pair;
 import org.apache.hyracks.algebricks.common.utils.Triple;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
-import org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
 import org.apache.hyracks.api.exceptions.SourceLocation;
 
 public class FunctionUtil {
@@ -69,21 +68,11 @@ public class FunctionUtil {
 
     private static final String FN_DATASET_NAME = BuiltinFunctions.DATASET.getName();
 
-    public static IFunctionInfo getFunctionInfo(FunctionIdentifier fi) {
-        return BuiltinFunctions.getAsterixFunctionInfo(fi);
-    }
-
-    public static IFunctionInfo getFunctionInfo(FunctionSignature fs) {
-        return getFunctionInfo(fs.createFunctionIdentifier());
-    }
-
-    public static IFunctionInfo getBuiltinFunctionInfo(String functionName, int arity) {
-        IFunctionInfo fi =
-                getFunctionInfo(new FunctionIdentifier(AlgebricksBuiltinFunctions.ALGEBRICKS_NS, functionName, arity));
-        if (fi == null) {
-            fi = getFunctionInfo(new FunctionIdentifier(FunctionConstants.ASTERIX_NS, functionName, arity));
-        }
-        return fi;
+    /**
+     * @deprecated use {@link BuiltinFunctions#getBuiltinFunctionInfo(FunctionIdentifier)} instead
+     */
+    public static BuiltinFunctionInfo getFunctionInfo(FunctionIdentifier fi) {
+        return BuiltinFunctions.getBuiltinFunctionInfo(fi);
     }
 
     public static TypeSignature getTypeDependencyFromFunctionParameter(TypeExpression typeExpr,
@@ -174,11 +163,21 @@ public class FunctionUtil {
     public static BiFunction<String, Integer, FunctionSignature> createBuiltinFunctionResolver(
             MetadataProvider metadataProvider) {
         boolean includePrivateFunctions = getImportPrivateFunctions(metadataProvider);
+        return createBuiltinFunctionResolver(includePrivateFunctions);
+    }
+
+    public static BiFunction<String, Integer, FunctionSignature> createBuiltinFunctionResolver(
+            boolean includePrivateFunctions) {
         return (name, arity) -> {
             String builtinName = name.replace('_', '-');
-            FunctionIdentifier builtinId =
-                    BuiltinFunctions.getBuiltinCompilerFunction(builtinName, arity, includePrivateFunctions);
-            return builtinId != null ? new FunctionSignature(builtinId) : null;
+            BuiltinFunctionInfo finfo = BuiltinFunctions.resolveBuiltinFunction(builtinName, arity);
+            if (finfo == null) {
+                return null;
+            }
+            if (!includePrivateFunctions && finfo.isPrivate()) {
+                return null;
+            }
+            return new FunctionSignature(finfo.getFunctionIdentifier());
         };
     }
 
@@ -244,13 +243,11 @@ public class FunctionUtil {
     }
 
     public static List<List<Triple<DataverseName, String, String>>> getFunctionDependencies(IQueryRewriter rewriter,
-            Expression expression, MetadataProvider metadataProvider, Collection<TypeSignature> dependentTypes)
-            throws CompilationException {
+            Expression expression, MetadataProvider metadataProvider) throws CompilationException {
         Set<CallExpr> functionCalls = rewriter.getFunctionCalls(expression);
         //Get the List of used functions and used datasets
         List<Triple<DataverseName, String, String>> datasourceDependencies = new ArrayList<>();
         List<Triple<DataverseName, String, String>> functionDependencies = new ArrayList<>();
-        List<Triple<DataverseName, String, String>> typeDependencies = new ArrayList<>(dependentTypes.size());
         for (CallExpr functionCall : functionCalls) {
             FunctionSignature signature = functionCall.getFunctionSignature();
             if (isBuiltinDatasetFunction(signature)) {
@@ -258,18 +255,15 @@ public class FunctionUtil {
                         metadataProvider.getDefaultDataverseName(), functionCall.getSourceLocation(),
                         ExpressionUtils::getStringLiteral);
                 datasourceDependencies.add(new Triple<>(datasetReference.first, datasetReference.second, null));
-            } else if (!BuiltinFunctions.isBuiltinCompilerFunction(signature, false)) {
+            } else if (BuiltinFunctions.getBuiltinFunctionInfo(signature.createFunctionIdentifier()) == null) {
                 functionDependencies.add(new Triple<>(signature.getDataverseName(), signature.getName(),
                         Integer.toString(signature.getArity())));
             }
         }
-        for (TypeSignature t : dependentTypes) {
-            typeDependencies.add(new Triple<>(t.getDataverseName(), t.getName(), null));
-        }
         List<List<Triple<DataverseName, String, String>>> dependencies = new ArrayList<>(3);
         dependencies.add(datasourceDependencies);
         dependencies.add(functionDependencies);
-        dependencies.add(typeDependencies);
+        dependencies.add(Collections.emptyList());
         return dependencies;
     }
 
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
index 3a329cc..325f9b0 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
@@ -787,7 +787,7 @@ public abstract class FormatPrintVisitor implements ILangVisitor<Void, Integer>
 
     @Override
     public Void visit(CreateFunctionStatement cfs, Integer step) throws CompilationException {
-        out.print(skip(step) + CREATE + " function ");
+        out.print(skip(step) + CREATE + generateOrReplace(cfs.getReplaceIfExists()) + " function ");
         out.print(generateIfNotExists(cfs.getIfNotExists()));
         out.print(this.generateFullName(cfs.getFunctionSignature().getDataverseName(),
                 cfs.getFunctionSignature().getName()));
@@ -1042,6 +1042,10 @@ public abstract class FormatPrintVisitor implements ILangVisitor<Void, Integer>
         return ifExits ? " if exists" : "";
     }
 
+    protected String generateOrReplace(boolean orReplace) {
+        return orReplace ? " or replace" : "";
+    }
+
     protected String generateIndexTypeString(IndexType type) {
         switch (type) {
             case BTREE:
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
index f83a89e..90fb9c1 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
@@ -163,7 +163,7 @@ public abstract class QueryPrintVisitor extends AbstractQueryExpressionVisitor<V
         return null;
     }
 
-    private static void printFunctionSignature(PrintWriter out, FunctionSignature fs, int arity) {
+    protected static void printFunctionSignature(PrintWriter out, FunctionSignature fs, int arity) {
         out.print(fs.toString(false));
         if (arity != FunctionIdentifier.VARARGS) {
             out.print("@");
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
index 7339a08..3c98a2d 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
@@ -178,13 +178,20 @@ public class SqlppQueryRewriter implements IQueryRewriter {
         // Inlines functions.
         inlineDeclaredUdfs(inlineUdfs);
 
+        // Rewrites SQL++ core aggregate function names into internal names
+        rewriteSpecialFunctionNames();
+
         // Inlines WITH expressions after variableCheckAndRewrite(...) so that the variable scoping for WITH
         // expression is correct.
+        //
+        // Must run after rewriteSpecialFunctionNames() because it needs to have FunctionInfo
+        // for all functions to avoid inlining non-deterministic expressions.
+        // (CallExprs with special function names do not have FunctionInfo)
+        //
+        // Must run after inlineDeclaredUdfs() because we only maintain deterministic modifiers for built-in
+        // and external UDFs, therefore need to inline SQL++ UDFs to check the deterministic property.
         inlineWithExpressions();
 
-        // Rewrites SQL++ core aggregate function names into internal names
-        rewriteSpecialFunctionNames();
-
         // Sets the var counter of the query.
         topStatement.setVarCounter(context.getVarCounter().get());
     }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckNonFunctionalExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckNonFunctionalExpressionVisitor.java
index bae21e4..d6ee1fc 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckNonFunctionalExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckNonFunctionalExpressionVisitor.java
@@ -23,10 +23,10 @@ import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.lang.common.expression.CallExpr;
-import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppContainsExpressionVisitor;
 import org.apache.asterix.metadata.declared.MetadataProvider;
 import org.apache.asterix.metadata.entities.Function;
+import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
 
@@ -44,19 +44,26 @@ public final class CheckNonFunctionalExpressionVisitor extends AbstractSqlppCont
     @Override
     public Boolean visit(CallExpr callExpr, Void arg) throws CompilationException {
         FunctionSignature fs = callExpr.getFunctionSignature();
-        IFunctionInfo fi = FunctionUtil.getBuiltinFunctionInfo(fs.getName(), fs.getArity());
-        if (fi != null) {
-            if (!fi.isFunctional()) {
+        IFunctionInfo finfo = BuiltinFunctions.getBuiltinFunctionInfo(fs.createFunctionIdentifier());
+        if (finfo != null) {
+            if (!finfo.isFunctional()) {
                 return true;
             }
         } else {
+            Function function;
             try {
-                Function function = metadataProvider.lookupUserDefinedFunction(fs);
-                if (function != null && function.getDeterministic() != null && !function.getDeterministic()) {
-                    return true;
-                }
+                function = metadataProvider.lookupUserDefinedFunction(fs);
             } catch (AlgebricksException e) {
-                throw new CompilationException(ErrorCode.METADATA_ERROR, e, callExpr.getSourceLocation());
+                throw new CompilationException(ErrorCode.METADATA_ERROR, e, callExpr.getSourceLocation(), e.toString());
+            }
+            if (function == null || function.getDeterministic() == null) {
+                // fail if function not found because all functions must have been resolved at this point
+                // fail if function does not define deterministic property (because it's a SQL++ function
+                // and they were supposed to be inlined at this point)
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, callExpr.getSourceLocation(), fs);
+            }
+            if (!function.getDeterministic()) {
+                return true;
             }
         }
         return super.visit(callExpr, arg);
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 9c5c1a4..038be3e 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
@@ -359,7 +359,9 @@ public class SqlppAstPrintVisitor extends QueryPrintVisitor implements ISqlppVis
 
     @Override
     public Void visit(WindowExpression winExpr, Integer step) throws CompilationException {
-        out.println(skip(step) + "WINDOW " + winExpr.getFunctionSignature() + "[");
+        out.print(skip(step) + "WINDOW ");
+        printFunctionSignature(out, winExpr.getFunctionSignature(), winExpr.getFunctionSignature().getArity());
+        out.println("[");
         for (Expression expr : winExpr.getExprList()) {
             expr.accept(this, step + 1);
         }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 5cdf1a7..525c15e 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -233,6 +233,7 @@ class SQLPPParser extends ScopeChecker implements IParser {
     private static final String SETS = "SETS";
     private static final String TIES = "TIES";
     private static final String UNBOUNDED = "UNBOUNDED";
+    private static final String REPLACE = "REPLACE";
     private static final String RETURNS = "RETURNS";
 
     private static final String INT_TYPE_NAME = "int";
@@ -464,12 +465,20 @@ class SQLPPParser extends ScopeChecker implements IParser {
     }
 
     private boolean isToken(String image) {
+      return isToken(token, image);
+    }
+
+    private static boolean isToken(Token token, String image) {
       return token.image.equalsIgnoreCase(image);
     }
 
     private void expectToken(String image) throws SqlppParseException {
-      if (!isToken(image)) {
-        throw createUnexpectedTokenError();
+      expectToken(token, image);
+    }
+
+    private static void expectToken(Token token, String image) throws SqlppParseException {
+      if (!isToken(token, image)) {
+        throw createUnexpectedTokenError(token);
       }
     }
 
@@ -477,7 +486,7 @@ class SQLPPParser extends ScopeChecker implements IParser {
       return createUnexpectedTokenError(token, null);
     }
 
-    private SqlppParseException createUnexpectedTokenError(Token t) {
+    private static SqlppParseException createUnexpectedTokenError(Token t) {
       return createUnexpectedTokenError(t, null);
     }
 
@@ -485,7 +494,7 @@ class SQLPPParser extends ScopeChecker implements IParser {
       return createUnexpectedTokenError(token, expected);
     }
 
-    private SqlppParseException createUnexpectedTokenError(Token t, String expected) {
+    private static SqlppParseException createUnexpectedTokenError(Token t, String expected) {
       String message = "Unexpected token: " + LogRedactionUtil.userData(t.image) +
         (expected == null ? "" : ". Expected: " + LogRedactionUtil.userData(expected));
       return new SqlppParseException(getSourceLocation(t), message);
@@ -652,12 +661,13 @@ Statement CreateStatement() throws ParseException:
 {
   <CREATE> { startToken = token; }
   (
-    stmt = CreateTypeStatement(startToken)
+    stmt = CreateOrReplaceStatement(startToken)
+    | stmt = CreateTypeStatement(startToken)
     | stmt = CreateNodegroupStatement(startToken)
     | stmt = CreateDatasetStatement(startToken)
     | stmt = CreateIndexStatement(startToken)
     | stmt = CreateDataverseStatement(startToken)
-    | stmt = CreateFunctionStatement(startToken)
+    | stmt = CreateFunctionStatement(startToken, false)
     | stmt = CreateAdapterStatement(startToken)
     | stmt = CreateSynonymStatement(startToken)
     | stmt = CreateFeedStatement(startToken)
@@ -668,6 +678,23 @@ Statement CreateStatement() throws ParseException:
   }
 }
 
+Statement CreateOrReplaceStatement(Token startStmtToken) throws ParseException:
+{
+  Statement stmt = null;
+  Token replaceToken = null;
+}
+{
+  <OR> <IDENTIFIER> { replaceToken = token; }
+  (
+    stmt = CreateFunctionStatement(startStmtToken, true)
+  )
+  {
+    // check expected token here to make the grammar extension plugin happy
+    expectToken(replaceToken, REPLACE);
+    return stmt;
+  }
+}
+
 TypeDecl CreateTypeStatement(Token startStmtToken) throws ParseException:
 {
   TypeDecl stmt = null;
@@ -1120,18 +1147,18 @@ CreateAdapterStatement AdapterSpecification(Token startStmtToken) throws ParseEx
   }
 }
 
-CreateFunctionStatement CreateFunctionStatement(Token startStmtToken) throws ParseException:
+CreateFunctionStatement CreateFunctionStatement(Token startStmtToken, boolean orReplace) throws ParseException:
 {
   CreateFunctionStatement stmt = null;
 }
 {
-  <FUNCTION> stmt = FunctionSpecification(startStmtToken)
+  <FUNCTION> stmt = FunctionSpecification(startStmtToken, orReplace)
   {
     return stmt;
   }
 }
 
-CreateFunctionStatement FunctionSpecification(Token startStmtToken) throws ParseException:
+CreateFunctionStatement FunctionSpecification(Token startStmtToken, boolean orReplace) throws ParseException:
 {
   FunctionSignature signature = null;
   FunctionName fctName = null;
@@ -1172,7 +1199,7 @@ CreateFunctionStatement FunctionSpecification(Token startStmtToken) throws Parse
         removeCurrentScope();
         defaultDataverse = currentDataverse;
         ensureNoTypeDeclsInFunction(fctName.function, params, returnType, startStmtToken);
-        stmt = new CreateFunctionStatement(signature, params, functionBody, functionBodyExpr, ifNotExists);
+        stmt = new CreateFunctionStatement(signature, params, functionBody, functionBodyExpr, orReplace, ifNotExists);
         return addSourceLocation(stmt, startStmtToken);
       }
     )
@@ -1186,7 +1213,7 @@ CreateFunctionStatement FunctionSpecification(Token startStmtToken) throws Parse
         defaultDataverse = currentDataverse;
         try {
           stmt = new CreateFunctionStatement(signature, params, returnType, libraryName.first,
-            libraryName.second.getValue(), externalIdentifier, withOptions, ifNotExists);
+            libraryName.second.getValue(), externalIdentifier, withOptions, orReplace, ifNotExists);
         } catch (AlgebricksException e) {
             throw new SqlppParseException(getSourceLocation(startStmtToken), e.getMessage());
         }
@@ -1340,7 +1367,6 @@ CreateFeedPolicyStatement FeedPolicySpecification(Token startStmtToken) throws P
   }
 }
 
-
 CreateSynonymStatement CreateSynonymStatement(Token startStmtToken) throws ParseException:
 {
   CreateSynonymStatement stmt = null;
@@ -1417,20 +1443,38 @@ String GetPolicy() throws ParseException:
 FunctionSignature FunctionSignature() throws ParseException:
 {
   FunctionName fctName = null;
+  List<Pair<VarIdentifier,TypeExpression>> params = null;
   int arity = 0;
 }
 {
-  fctName = FunctionName() <ATT> <INTEGER_LITERAL>
-    {
-      arity = new Integer(token.image);
-      if (arity < 0 && arity != FunctionIdentifier.VARARGS) {
-        throw new SqlppParseException(getSourceLocation(token), "Invalid arity:" + arity);
-      }
+  fctName = FunctionName()
+  (
+    LOOKAHEAD(2) params = FunctionParameters() { arity = params.size(); }
+  | ( <LEFTPAREN> arity = FunctionArity() <RIGHTPAREN> )
+  | ( <ATT> arity = FunctionArity() ) // back-compat
+  )
+  {
+    return new FunctionSignature(fctName.dataverse, fctName.function, arity);
+  }
+}
 
-      // TODO use fctName.library
-      String fqFunctionName = fctName.library == null ? fctName.function : fctName.library + "#" + fctName.function;
-      return new FunctionSignature(fctName.dataverse, fqFunctionName, arity);
+int FunctionArity() throws ParseException:
+{
+  int arity;
+}
+{
+  <INTEGER_LITERAL>
+  {
+    try {
+      arity = Integer.parseInt(token.image);
+    } catch (NumberFormatException e) {
+      throw new SqlppParseException(getSourceLocation(token), "Invalid arity: " + token.image);
     }
+    if (arity < 0 && arity != FunctionIdentifier.VARARGS) {
+      throw new SqlppParseException(getSourceLocation(token), "Invalid arity: " + arity);
+    }
+    return arity;
+  }
 }
 
 Pair<List<Integer>, List<List<String>>> PrimaryKey() throws ParseException:
diff --git a/asterixdb/asterix-maven-plugins/asterix-grammar-extension-maven-plugin/src/main/java/org/apache/asterix/extension/grammar/GrammarExtensionMojo.java b/asterixdb/asterix-maven-plugins/asterix-grammar-extension-maven-plugin/src/main/java/org/apache/asterix/extension/grammar/GrammarExtensionMojo.java
index 52b2be5..d47e9a6 100644
--- a/asterixdb/asterix-maven-plugins/asterix-grammar-extension-maven-plugin/src/main/java/org/apache/asterix/extension/grammar/GrammarExtensionMojo.java
+++ b/asterixdb/asterix-maven-plugins/asterix-grammar-extension-maven-plugin/src/main/java/org/apache/asterix/extension/grammar/GrammarExtensionMojo.java
@@ -25,6 +25,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.LineNumberReader;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
@@ -324,7 +325,7 @@ public class GrammarExtensionMojo extends AbstractMojo {
         }
         String innerBlock2String = null;
         if (baseBlocks.second != null) {
-            BufferedReader blockReader = stringToReader(baseBlocks.second);
+            LineNumberReader blockReader = stringToReader(baseBlocks.second);
             Position blockPosition = new Position();
             blockPosition.index = 0;
             blockPosition.line = blockReader.readLine();
@@ -337,7 +338,8 @@ public class GrammarExtensionMojo extends AbstractMojo {
                 blockPosition.line = blockReader.readLine();
             }
             if (blockPosition.line == null) {
-                throw new MojoExecutionException(errorMessage);
+                throw new MojoExecutionException(errorMessage + " at line " + blockReader.getLineNumber() + " of "
+                        + StringUtils.abbreviate(baseBlocks.second, 100));
             }
             int block2Open = blockPosition.line.indexOf(OPEN_BRACE);
             blockPosition.line = blockPosition.line.substring(block2Open + 1);
@@ -350,7 +352,8 @@ public class GrammarExtensionMojo extends AbstractMojo {
                 blockPosition.line = blockReader.readLine();
             }
             if (blockPosition.line == null) {
-                throw new MojoExecutionException(errorMessage);
+                throw new MojoExecutionException(errorMessage + " at line " + blockReader.getLineNumber() + " of "
+                        + StringUtils.abbreviate(baseBlocks.second, 100));
             }
             int innerBlock1Open = blockPosition.line.indexOf(OPEN_PAREN);
             writer.write("  ");
@@ -372,7 +375,8 @@ public class GrammarExtensionMojo extends AbstractMojo {
             }
             int innerBlock2Open = blockPosition.line.indexOf(OPEN_BRACE);
             if (innerBlock2Open < 0) {
-                throw new MojoExecutionException(errorMessage);
+                throw new MojoExecutionException(errorMessage + " at line " + blockReader.getLineNumber() + " of "
+                        + StringUtils.abbreviate(baseBlocks.second, 100));
             }
             blockPosition.index = innerBlock2Open;
             readBlock(blockReader, OPEN_BRACE, CLOSE_BRACE, blockPosition);
@@ -989,9 +993,9 @@ public class GrammarExtensionMojo extends AbstractMojo {
         }
     }
 
-    private BufferedReader stringToReader(String aString) {
+    private LineNumberReader stringToReader(String aString) {
         InputStream is = new ByteArrayInputStream(aString.getBytes(StandardCharsets.UTF_8));
-        return new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
+        return new LineNumberReader(new InputStreamReader(is, StandardCharsets.UTF_8));
     }
 
     private File prepareOutputFile() throws MojoExecutionException {
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java
index afc0dc5..268a9c0 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java
@@ -576,7 +576,7 @@ public abstract class MetadataManager implements IMetadataManager {
     public Function getFunction(MetadataTransactionContext ctx, FunctionSignature functionSignature)
             throws AlgebricksException {
         // First look in the context to see if this transaction created the
-        // requested dataset itself (but the dataset is still uncommitted).
+        // requested function itself (but the function is still uncommitted).
         Function function = ctx.getFunction(functionSignature);
         if (function != null) {
             // Don't add this dataverse to the cache, since it is still
@@ -984,6 +984,30 @@ public abstract class MetadataManager implements IMetadataManager {
     }
 
     @Override
+    public void updateFunction(MetadataTransactionContext ctx, Function function) throws AlgebricksException {
+        try {
+            metadataNode.updateFunction(ctx.getTxnId(), function);
+        } catch (RemoteException e) {
+            throw new MetadataException(ErrorCode.REMOTE_EXCEPTION_WHEN_CALLING_METADATA_NODE, e);
+        }
+        // reflect the function into the cache
+        ctx.dropFunction(function.getSignature());
+        ctx.addFunction(function);
+    }
+
+    @Override
+    public void updateDatatype(MetadataTransactionContext ctx, Datatype datatype) throws AlgebricksException {
+        try {
+            metadataNode.updateDatatype(ctx.getTxnId(), datatype);
+        } catch (RemoteException e) {
+            throw new MetadataException(ErrorCode.REMOTE_EXCEPTION_WHEN_CALLING_METADATA_NODE, e);
+        }
+        // reflect the datatype into the cache
+        ctx.dropDataDatatype(datatype.getDataverseName(), datatype.getDatatypeName());
+        ctx.addDatatype(datatype);
+    }
+
+    @Override
     public <T extends IExtensionMetadataEntity> void addEntity(MetadataTransactionContext mdTxnCtx, T entity)
             throws AlgebricksException {
         try {
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java
index 07dd4e5..88098f8 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java
@@ -86,6 +86,7 @@ import org.apache.asterix.metadata.entitytupletranslators.NodeGroupTupleTranslat
 import org.apache.asterix.metadata.entitytupletranslators.NodeTupleTranslator;
 import org.apache.asterix.metadata.entitytupletranslators.SynonymTupleTranslator;
 import org.apache.asterix.metadata.utils.DatasetUtil;
+import org.apache.asterix.metadata.utils.TypeUtil;
 import org.apache.asterix.metadata.valueextractors.MetadataEntityValueExtractor;
 import org.apache.asterix.metadata.valueextractors.TupleCopyValueExtractor;
 import org.apache.asterix.om.base.AInt32;
@@ -433,15 +434,12 @@ public class MetadataNode implements IMetadataNode {
             // Insert into the 'function' dataset.
             FunctionTupleTranslator tupleReaderWriter =
                     tupleTranslatorProvider.getFunctionTupleTranslator(txnId, this, true);
-
             ITupleReference functionTuple = tupleReaderWriter.getTupleFromMetadataEntity(function);
             insertTupleIntoIndex(txnId, MetadataPrimaryIndexes.FUNCTION_DATASET, functionTuple);
-
         } catch (HyracksDataException e) {
             if (e.getComponent().equals(ErrorCode.HYRACKS) && e.getErrorCode() == ErrorCode.DUPLICATE_KEY) {
-                throw new AlgebricksException("A function with this name " + function.getName() + " and arity "
-                        + function.getArity() + " already exists in dataverse '" + function.getDataverseName() + "'.",
-                        e);
+                throw new AlgebricksException("A function with this name " + function.getSignature()
+                        + " already exists in dataverse '" + function.getDataverseName() + "'.", e);
             } else {
                 throw new AlgebricksException(e);
             }
@@ -547,8 +545,7 @@ public class MetadataNode implements IMetadataNode {
             // As a side effect, acquires an S lock on the 'Function' dataset on behalf of txnId.
             List<Function> dataverseFunctions = getDataverseFunctions(txnId, dataverseName);
             for (Function function : dataverseFunctions) {
-                dropFunction(txnId, new FunctionSignature(dataverseName, function.getName(), function.getArity()),
-                        true);
+                dropFunction(txnId, function.getSignature(), true);
             }
 
             // Drop all adapters in this dataverse.
@@ -721,9 +718,14 @@ public class MetadataNode implements IMetadataNode {
 
     @Override
     public void dropDatatype(TxnId txnId, DataverseName dataverseName, String datatypeName) throws AlgebricksException {
+        dropDatatype(txnId, dataverseName, datatypeName, false);
+    }
 
-        confirmDatatypeIsUnused(txnId, dataverseName, datatypeName);
-
+    private void dropDatatype(TxnId txnId, DataverseName dataverseName, String datatypeName, boolean force)
+            throws AlgebricksException {
+        if (!force) {
+            confirmDatatypeIsUnused(txnId, dataverseName, datatypeName);
+        }
         // Delete the datatype entry, including all it's nested anonymous types.
         try {
             ITupleReference searchKey = createTuple(dataverseName, datatypeName);
@@ -888,16 +890,7 @@ public class MetadataNode implements IMetadataNode {
     }
 
     public List<Function> getAllFunctions(TxnId txnId) throws AlgebricksException {
-        try {
-            FunctionTupleTranslator tupleReaderWriter =
-                    tupleTranslatorProvider.getFunctionTupleTranslator(txnId, this, false);
-            IValueExtractor<Function> valueExtractor = new MetadataEntityValueExtractor<>(tupleReaderWriter);
-            List<Function> results = new ArrayList<>();
-            searchIndex(txnId, MetadataPrimaryIndexes.FUNCTION_DATASET, null, valueExtractor, results);
-            return results;
-        } catch (HyracksDataException e) {
-            throw new AlgebricksException(e);
-        }
+        return getFunctionsImpl(txnId, null);
     }
 
     public List<Datatype> getAllDatatypes(TxnId txnId) throws AlgebricksException {
@@ -949,15 +942,16 @@ public class MetadataNode implements IMetadataNode {
                 continue;
             }
             if (set.getItemTypeDataverseName().equals(dataverseName)) {
-                throw new AlgebricksException(
-                        "Cannot drop dataverse. Type " + dataverseName + "." + set.getItemTypeName()
-                                + " used by dataset " + set.getDataverseName() + "." + set.getDatasetName());
+                throw new AlgebricksException("Cannot drop dataverse. Type "
+                        + TypeUtil.getFullyQualifiedDisplayName(set.getItemTypeDataverseName(), set.getItemTypeName())
+                        + " used by dataset " + DatasetUtil.getFullyQualifiedDisplayName(set));
             }
             if (set.getMetaItemTypeDataverseName() != null
                     && set.getMetaItemTypeDataverseName().equals(dataverseName)) {
-                throw new AlgebricksException(
-                        "Cannot drop dataverse. Type " + dataverseName + "." + set.getMetaItemTypeName()
-                                + " used by dataset " + set.getDataverseName() + "." + set.getDatasetName());
+                throw new AlgebricksException("Cannot drop dataverse. Type "
+                        + TypeUtil.getFullyQualifiedDisplayName(set.getMetaItemTypeDataverseName(),
+                                set.getMetaItemTypeName())
+                        + " used by dataset " + DatasetUtil.getFullyQualifiedDisplayName(set));
             }
         }
 
@@ -971,24 +965,22 @@ public class MetadataNode implements IMetadataNode {
             }
             for (Triple<DataverseName, String, String> datasetDependency : function.getDependencies().get(0)) {
                 if (datasetDependency.first.equals(dataverseName)) {
-                    throw new AlgebricksException("Cannot drop dataverse. Function " + function.getDataverseName() + "."
-                            + function.getName() + "@" + function.getArity() + " depends on dataset "
-                            + datasetDependency.first + "." + datasetDependency.second);
+                    throw new AlgebricksException("Cannot drop dataverse. Function " + function.getSignature()
+                            + " depends on dataset " + DatasetUtil.getFullyQualifiedDisplayName(datasetDependency.first,
+                                    datasetDependency.second));
                 }
             }
             for (Triple<DataverseName, String, String> functionDependency : function.getDependencies().get(1)) {
                 if (functionDependency.first.equals(dataverseName)) {
-                    throw new AlgebricksException(
-                            "Cannot drop dataverse. Function " + function.getDataverseName() + "." + function.getName()
-                                    + "@" + function.getArity() + " depends on function " + functionDependency.first
-                                    + "." + functionDependency.second + "@" + functionDependency.third);
+                    throw new AlgebricksException("Cannot drop dataverse. Function " + function.getSignature()
+                            + " depends on function " + new FunctionSignature(functionDependency.first,
+                                    functionDependency.second, Integer.parseInt(functionDependency.third)));
                 }
             }
             for (Triple<DataverseName, String, String> type : function.getDependencies().get(2)) {
                 if (type.first.equals(dataverseName)) {
-                    throw new AlgebricksException(
-                            "Cannot drop dataverse. Function " + function.getDataverseName() + "." + function.getName()
-                                    + "@" + function.getArity() + " depends on type " + type.first + "." + type.second);
+                    throw new AlgebricksException("Cannot drop dataverse. Function " + function.getSignature()
+                            + " depends on type " + TypeUtil.getFullyQualifiedDisplayName(type.first, type.second));
                 }
             }
         }
@@ -1002,10 +994,9 @@ public class MetadataNode implements IMetadataNode {
             }
             for (FunctionSignature functionSignature : feedConnection.getAppliedFunctions()) {
                 if (dataverseName.equals(functionSignature.getDataverseName())) {
-                    throw new AlgebricksException("Cannot drop dataverse. Feed connection "
-                            + feedConnection.getDataverseName() + "." + feedConnection.getFeedName()
-                            + " depends on function " + functionSignature.getDataverseName() + "."
-                            + functionSignature.getName() + "@" + functionSignature.getArity());
+                    throw new AlgebricksException(
+                            "Cannot drop dataverse. Feed connection " + feedConnection.getDataverseName() + "."
+                                    + feedConnection.getFeedName() + " depends on function " + functionSignature);
                 }
             }
         }
@@ -1019,8 +1010,8 @@ public class MetadataNode implements IMetadataNode {
                 if (functionalDependency.first.equals(signature.getDataverseName())
                         && functionalDependency.second.equals(signature.getName())
                         && functionalDependency.third.equals(Integer.toString(signature.getArity()))) {
-                    throw new AlgebricksException("Cannot drop function " + signature + " being used by function "
-                            + function.getDataverseName() + "." + function.getName() + "@" + function.getArity());
+                    throw new AlgebricksException(
+                            "Cannot drop function " + signature + " being used by function " + function.getSignature());
                 }
             }
         }
@@ -1044,8 +1035,7 @@ public class MetadataNode implements IMetadataNode {
                 if (datasetDependency.first.equals(dataverseName) && datasetDependency.second.equals(datasetName)) {
                     throw new AlgebricksException("Cannot drop dataset "
                             + DatasetUtil.getFullyQualifiedDisplayName(dataverseName, datasetName)
-                            + " being used by function " + function.getDataverseName() + "." + function.getName() + "@"
-                            + function.getArity());
+                            + " being used by function " + function.getSignature());
                 }
             }
         }
@@ -1063,9 +1053,8 @@ public class MetadataNode implements IMetadataNode {
         for (Function function : functions) {
             if (libraryName.equals(function.getLibraryName())
                     && dataverseName.equals(function.getLibraryDataverseName())) {
-                throw new AlgebricksException(
-                        "Cannot drop library " + dataverseName + '.' + libraryName + " being used by funciton "
-                                + function.getDataverseName() + '.' + function.getName() + '@' + function.getArity());
+                throw new AlgebricksException("Cannot drop library " + dataverseName + '.' + libraryName
+                        + " being used by funciton " + function.getSignature());
             }
         }
     }
@@ -1096,8 +1085,9 @@ public class MetadataNode implements IMetadataNode {
         List<Dataset> datasets = getAllDatasets(txnId);
         for (Dataset set : datasets) {
             if (set.getItemTypeName().equals(datatypeName) && set.getItemTypeDataverseName().equals(dataverseName)) {
-                throw new AlgebricksException("Cannot drop type " + dataverseName + "." + datatypeName
-                        + " being used by dataset " + set.getDataverseName() + "." + set.getDatasetName());
+                throw new AlgebricksException(
+                        "Cannot drop type " + TypeUtil.getFullyQualifiedDisplayName(dataverseName, datatypeName)
+                                + " being used by dataset " + DatasetUtil.getFullyQualifiedDisplayName(set));
             }
         }
     }
@@ -1119,8 +1109,9 @@ public class MetadataNode implements IMetadataNode {
             }
             AbstractComplexType recType = (AbstractComplexType) dataType.getDatatype();
             if (recType.containsType(typeToBeDropped)) {
-                throw new AlgebricksException("Cannot drop type " + dataverseName + "." + datatypeName
-                        + " being used by type " + dataverseName + "." + recType.getTypeName());
+                throw new AlgebricksException("Cannot drop type "
+                        + TypeUtil.getFullyQualifiedDisplayName(dataverseName, datatypeName) + " being used by type "
+                        + TypeUtil.getFullyQualifiedDisplayName(dataverseName, recType.getTypeName()));
             }
         }
     }
@@ -1132,9 +1123,9 @@ public class MetadataNode implements IMetadataNode {
         for (Function function : functions) {
             for (Triple<DataverseName, String, String> datasetDependency : function.getDependencies().get(2)) {
                 if (datasetDependency.first.equals(dataverseName) && datasetDependency.second.equals(dataTypeName)) {
-                    throw new AlgebricksException("Cannot drop type " + dataverseName + "." + dataTypeName
-                            + " is being used by function " + function.getDataverseName() + "." + function.getName()
-                            + "@" + function.getArity());
+                    throw new AlgebricksException(
+                            "Cannot drop type " + TypeUtil.getFullyQualifiedDisplayName(dataverseName, dataTypeName)
+                                    + " is being used by function " + function.getSignature());
                 }
             }
         }
@@ -1253,27 +1244,18 @@ public class MetadataNode implements IMetadataNode {
 
     @Override
     public Function getFunction(TxnId txnId, FunctionSignature functionSignature) throws AlgebricksException {
-        try {
-            ITupleReference searchKey = createTuple(functionSignature.getDataverseName(), functionSignature.getName(),
-                    Integer.toString(functionSignature.getArity()));
-            FunctionTupleTranslator tupleReaderWriter =
-                    tupleTranslatorProvider.getFunctionTupleTranslator(txnId, this, false);
-            List<Function> results = new ArrayList<>();
-            IValueExtractor<Function> valueExtractor = new MetadataEntityValueExtractor<>(tupleReaderWriter);
-            searchIndex(txnId, MetadataPrimaryIndexes.FUNCTION_DATASET, searchKey, valueExtractor, results);
-            if (results.isEmpty()) {
-                return null;
-            }
-            return results.get(0);
-        } catch (HyracksDataException e) {
-            throw new AlgebricksException(e);
-        }
+        List<Function> functions = getFunctionsImpl(txnId, createTuple(functionSignature.getDataverseName(),
+                functionSignature.getName(), Integer.toString(functionSignature.getArity())));
+        return functions.isEmpty() ? null : functions.get(0);
     }
 
     @Override
     public List<Function> getDataverseFunctions(TxnId txnId, DataverseName dataverseName) throws AlgebricksException {
+        return getFunctionsImpl(txnId, createTuple(dataverseName));
+    }
+
+    private List<Function> getFunctionsImpl(TxnId txnId, ITupleReference searchKey) throws AlgebricksException {
         try {
-            ITupleReference searchKey = createTuple(dataverseName);
             FunctionTupleTranslator tupleReaderWriter =
                     tupleTranslatorProvider.getFunctionTupleTranslator(txnId, this, false);
             List<Function> results = new ArrayList<>();
@@ -1295,25 +1277,19 @@ public class MetadataNode implements IMetadataNode {
         if (!force) {
             confirmFunctionCanBeDeleted(txnId, functionSignature);
         }
-        Function function = getFunction(txnId, functionSignature);
-        if (function == null) {
-            throw new AlgebricksException(
-                    "Cannot drop function '" + functionSignature.toString() + "' because it doesn't exist.");
-        }
         try {
             // Delete entry from the 'function' dataset.
             ITupleReference searchKey = createTuple(functionSignature.getDataverseName(), functionSignature.getName(),
                     Integer.toString(functionSignature.getArity()));
-            // Searches the index for the tuple to be deleted. Acquires an S
-            // lock on the 'function' dataset.
+            // Searches the index for the tuple to be deleted. Acquires an S lock on the 'function' dataset.
             ITupleReference functionTuple =
                     getTupleToBeDeleted(txnId, MetadataPrimaryIndexes.FUNCTION_DATASET, searchKey);
             deleteTupleFromIndex(txnId, MetadataPrimaryIndexes.FUNCTION_DATASET, functionTuple);
         } catch (HyracksDataException e) {
             if (e.getComponent().equals(ErrorCode.HYRACKS)
                     && e.getErrorCode() == ErrorCode.UPDATE_OR_DELETE_NON_EXISTENT_KEY) {
-                throw new AlgebricksException("There is no function with the name " + functionSignature.getName()
-                        + " and arity " + functionSignature.getArity(), e);
+                throw new AlgebricksException(
+                        "Cannot drop function '" + functionSignature + "' because it doesn't exist", e);
             } else {
                 throw new AlgebricksException(e);
             }
@@ -1661,7 +1637,6 @@ public class MetadataNode implements IMetadataNode {
             LibraryTupleTranslator tupleReaderWriter = tupleTranslatorProvider.getLibraryTupleTranslator(true);
             ITupleReference libraryTuple = tupleReaderWriter.getTupleFromMetadataEntity(library);
             insertTupleIntoIndex(txnId, MetadataPrimaryIndexes.LIBRARY_DATASET, libraryTuple);
-
         } catch (HyracksDataException e) {
             if (e.getComponent().equals(ErrorCode.HYRACKS) && e.getErrorCode() == ErrorCode.DUPLICATE_KEY) {
                 throw new AlgebricksException("A library with this name " + library.getDataverseName()
@@ -1674,13 +1649,18 @@ public class MetadataNode implements IMetadataNode {
 
     @Override
     public void dropLibrary(TxnId txnId, DataverseName dataverseName, String libraryName) throws AlgebricksException {
-        confirmLibraryCanBeDeleted(txnId, dataverseName, libraryName);
+        dropLibrary(txnId, dataverseName, libraryName, false);
+    }
 
+    private void dropLibrary(TxnId txnId, DataverseName dataverseName, String libraryName, boolean force)
+            throws AlgebricksException {
+        if (!force) {
+            confirmLibraryCanBeDeleted(txnId, dataverseName, libraryName);
+        }
         try {
             // Delete entry from the 'Library' dataset.
             ITupleReference searchKey = createTuple(dataverseName, libraryName);
-            // Searches the index for the tuple to be deleted. Acquires an S
-            // lock on the 'Adapter' dataset.
+            // Searches the index for the tuple to be deleted. Acquires an S lock on the 'Library' dataset.
             ITupleReference datasetTuple =
                     getTupleToBeDeleted(txnId, MetadataPrimaryIndexes.LIBRARY_DATASET, searchKey);
             deleteTupleFromIndex(txnId, MetadataPrimaryIndexes.LIBRARY_DATASET, datasetTuple);
@@ -2101,40 +2081,41 @@ public class MetadataNode implements IMetadataNode {
             // This method will delete previous entry of the dataset and insert the new one
             // Delete entry from the 'datasets' dataset.
             ITupleReference searchKey = createTuple(dataset.getDataverseName(), dataset.getDatasetName());
-            // Searches the index for the tuple to be deleted. Acquires an S
-            // lock on the 'dataset' dataset.
+            // Searches the index for the tuple to be deleted. Acquires an S lock on the 'dataset' dataset.
             ITupleReference datasetTuple =
                     getTupleToBeDeleted(txnId, MetadataPrimaryIndexes.DATASET_DATASET, searchKey);
             deleteTupleFromIndex(txnId, MetadataPrimaryIndexes.DATASET_DATASET, datasetTuple);
-            // Previous tuple was deleted
             // Insert into the 'dataset' dataset.
             DatasetTupleTranslator tupleReaderWriter = tupleTranslatorProvider.getDatasetTupleTranslator(true);
             datasetTuple = tupleReaderWriter.getTupleFromMetadataEntity(dataset);
             insertTupleIntoIndex(txnId, MetadataPrimaryIndexes.DATASET_DATASET, datasetTuple);
         } catch (HyracksDataException e) {
-            throw new AlgebricksException(e);
+            if (e.getComponent().equals(ErrorCode.HYRACKS)
+                    && e.getErrorCode() == ErrorCode.UPDATE_OR_DELETE_NON_EXISTENT_KEY) {
+                throw new AlgebricksException(
+                        "Cannot drop dataset '" + dataset.getDatasetName() + "' because it doesn't exist");
+            } else {
+                throw new AlgebricksException(e);
+            }
         }
     }
 
     @Override
     public void updateLibrary(TxnId txnId, Library library) throws AlgebricksException {
-        try {
-            // This method will delete previous entry of the library and insert the new one
-            // Delete entry from the 'library' dataset.
-            ITupleReference searchKey = createTuple(library.getDataverseName(), library.getName());
-            // Searches the index for the tuple to be deleted. Acquires an S
-            // lock on the 'library' dataset.
-            ITupleReference libraryTuple =
-                    getTupleToBeDeleted(txnId, MetadataPrimaryIndexes.LIBRARY_DATASET, searchKey);
-            deleteTupleFromIndex(txnId, MetadataPrimaryIndexes.LIBRARY_DATASET, libraryTuple);
-            // Previous tuple was deleted
-            // Insert into the 'library' dataset.
-            LibraryTupleTranslator tupleReaderWriter = tupleTranslatorProvider.getLibraryTupleTranslator(true);
-            libraryTuple = tupleReaderWriter.getTupleFromMetadataEntity(library);
-            insertTupleIntoIndex(txnId, MetadataPrimaryIndexes.LIBRARY_DATASET, libraryTuple);
-        } catch (HyracksDataException e) {
-            throw new AlgebricksException(e);
-        }
+        dropLibrary(txnId, library.getDataverseName(), library.getName(), true);
+        addLibrary(txnId, library);
+    }
+
+    @Override
+    public void updateFunction(TxnId txnId, Function function) throws AlgebricksException {
+        dropFunction(txnId, function.getSignature(), true);
+        addFunction(txnId, function);
+    }
+
+    @Override
+    public void updateDatatype(TxnId txnId, Datatype datatype) throws AlgebricksException {
+        dropDatatype(txnId, datatype.getDataverseName(), datatype.getDatatypeName(), true);
+        addDatatype(txnId, datatype);
     }
 
     public ITxnIdFactory getTxnIdFactory() {
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataManager.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataManager.java
index da4d2fb..1646a93 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataManager.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataManager.java
@@ -723,6 +723,24 @@ public interface IMetadataManager extends IMetadataBootstrap {
     void updateLibrary(MetadataTransactionContext ctx, Library library) throws AlgebricksException;
 
     /**
+     * @param mdTxnCtx
+     *            MetadataTransactionContext of an active metadata transaction.
+     * @param function
+     *            An instance of type Function that represents the function being
+     *            updated
+     */
+    void updateFunction(MetadataTransactionContext mdTxnCtx, Function function) throws AlgebricksException;
+
+    /**
+     * @param mdTxnCtx
+     *            MetadataTransactionContext of an active metadata transaction.
+     * @param datatype
+     *            An instance of type Datatype that represents the datatype being
+     *            updated
+     */
+    void updateDatatype(MetadataTransactionContext mdTxnCtx, Datatype datatype) throws AlgebricksException;
+
+    /**
      * Add an extension entity to its extension dataset under the ongoing metadata
      * transaction
      *
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataNode.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataNode.java
index 6a450f2..bca4171 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataNode.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/api/IMetadataNode.java
@@ -824,6 +824,28 @@ public interface IMetadataNode extends Remote, Serializable {
     void updateLibrary(TxnId txnId, Library library) throws AlgebricksException, RemoteException;
 
     /**
+     * update an existing function in the metadata, acquiring local locks on behalf
+     * of the given transaction id.
+     *
+     * @param txnId
+     *            A globally unique id for an active metadata transaction.
+     * @param function
+     *            updated Function instance.
+     */
+    void updateFunction(TxnId txnId, Function function) throws AlgebricksException, RemoteException;
+
+    /**
+     * update an existing datatype in the metadata, acquiring local locks on behalf
+     * of the given transaction id.
+     *
+     * @param txnId
+     *            A globally unique id for an active metadata transaction.
+     * @param datatype
+     *            updated Datatype instance.
+     */
+    void updateDatatype(TxnId txnId, Datatype datatype) throws AlgebricksException, RemoteException;
+
+    /**
      * Adds an extension entity under the ongoing transaction job id
      *
      * @param txnId
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/FunctionDataSource.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/FunctionDataSource.java
index 93927dd..1303b21 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/FunctionDataSource.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/FunctionDataSource.java
@@ -52,11 +52,19 @@ import org.apache.hyracks.storage.am.common.api.ITupleFilterFactory;
 
 public abstract class FunctionDataSource extends DataSource {
 
-    public FunctionDataSource(DataSourceId id, INodeDomain domain) throws AlgebricksException {
+    protected final FunctionIdentifier functionId;
+
+    public FunctionDataSource(DataSourceId id, FunctionIdentifier functionId, INodeDomain domain)
+            throws AlgebricksException {
         super(id, RecordUtil.FULLY_OPEN_RECORD_TYPE, null, DataSource.Type.FUNCTION, domain);
+        this.functionId = functionId;
         schemaTypes = new IAType[] { itemType };
     }
 
+    public FunctionIdentifier getFunctionId() {
+        return functionId;
+    }
+
     @Override
     public boolean isScanAccessPathALeaf() {
         return true;
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
index 8a16014..4c19b61 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
@@ -446,7 +446,7 @@ public class MetadataProvider implements IMetadataProvider<DataSourceId, String>
 
     @Override
     public IFunctionInfo lookupFunction(FunctionIdentifier fid) {
-        return BuiltinFunctions.lookupFunction(fid);
+        return BuiltinFunctions.getBuiltinFunctionInfo(fid);
     }
 
     public Function lookupUserDefinedFunction(FunctionSignature signature) throws AlgebricksException {
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Dataset.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Dataset.java
index cc4bb09..e66057a 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Dataset.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Dataset.java
@@ -62,6 +62,7 @@ import org.apache.asterix.metadata.utils.IndexUtil;
 import org.apache.asterix.metadata.utils.InvertedIndexResourceFactoryProvider;
 import org.apache.asterix.metadata.utils.MetadataUtil;
 import org.apache.asterix.metadata.utils.RTreeResourceFactoryProvider;
+import org.apache.asterix.metadata.utils.TypeUtil;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.IAType;
@@ -424,6 +425,14 @@ public class Dataset implements IMetadataEntity<Dataset>, IDataset {
         // #. finally, delete the dataset.
         MetadataManager.INSTANCE.dropDataset(mdTxnCtx.getValue(), dataverseName, datasetName);
 
+        // drop inline types
+        if (TypeUtil.isDatasetInlineTypeName(this, recordTypeDataverseName, recordTypeName)) {
+            MetadataManager.INSTANCE.dropDatatype(mdTxnCtx.getValue(), recordTypeDataverseName, recordTypeName);
+        }
+        if (hasMetaPart() && TypeUtil.isDatasetInlineTypeName(this, metaTypeDataverseName, metaTypeName)) {
+            MetadataManager.INSTANCE.dropDatatype(mdTxnCtx.getValue(), metaTypeDataverseName, metaTypeName);
+        }
+
         // Drops the associated nodegroup if it is no longer used by any other dataset.
         if (dropCorrespondingNodeGroup) {
             metadataProvider.getApplicationContext().getMetadataLockManager()
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java
index 2b0fde7..968cf14 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java
@@ -91,18 +91,25 @@ public class Function implements IMetadataEntity<Function> {
         return paramNames;
     }
 
+    /**
+     * @return {@code null} for non-external functions;
+     *  for external function the list may contain {@code null} which means 'any' type
+     */
     public List<TypeSignature> getParameterTypes() {
         return paramTypes;
     }
 
-    public String getFunctionBody() {
-        return body;
-    }
-
+    /**
+     * @return {@code null} for non-external functions
+     */
     public TypeSignature getReturnType() {
         return returnType;
     }
 
+    public String getFunctionBody() {
+        return body;
+    }
+
     public String getLanguage() {
         return language;
     }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/AbstractTupleTranslator.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/AbstractTupleTranslator.java
index 16137ef..a35246f 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/AbstractTupleTranslator.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/AbstractTupleTranslator.java
@@ -32,6 +32,7 @@ import org.apache.asterix.om.base.ABoolean;
 import org.apache.asterix.om.base.AInt32;
 import org.apache.asterix.om.base.AInt64;
 import org.apache.asterix.om.base.AMutableString;
+import org.apache.asterix.om.base.ANull;
 import org.apache.asterix.om.base.ARecord;
 import org.apache.asterix.om.base.AString;
 import org.apache.asterix.om.types.ARecordType;
@@ -51,6 +52,9 @@ import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
 public abstract class AbstractTupleTranslator<T> implements IMetadataEntityTupleTranslator<T> {
 
     @SuppressWarnings("unchecked")
+    protected final ISerializerDeserializer<ANull> nullSerde =
+            SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ANULL);
+    @SuppressWarnings("unchecked")
     protected final ISerializerDeserializer<AString> stringSerde =
             SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ASTRING);
     @SuppressWarnings("unchecked")
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java
index 9c8d678..dddd28e 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java
@@ -57,12 +57,13 @@ import org.apache.asterix.metadata.bootstrap.MetadataPrimaryIndexes;
 import org.apache.asterix.metadata.bootstrap.MetadataRecordTypes;
 import org.apache.asterix.metadata.entities.BuiltinTypeMap;
 import org.apache.asterix.metadata.entities.Function;
-import org.apache.asterix.metadata.utils.TypeUtil;
 import org.apache.asterix.om.base.ABoolean;
+import org.apache.asterix.om.base.ANull;
 import org.apache.asterix.om.base.AOrderedList;
 import org.apache.asterix.om.base.ARecord;
 import org.apache.asterix.om.base.AString;
 import org.apache.asterix.om.base.IACursor;
+import org.apache.asterix.om.base.IAObject;
 import org.apache.asterix.om.pointables.base.DefaultOpenFieldType;
 import org.apache.asterix.om.types.AOrderedListType;
 import org.apache.asterix.om.types.ARecordType;
@@ -120,12 +121,17 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
             paramNames.add(((AString) paramNameCursor.get()).getStringValue());
         }
 
-        List<TypeSignature> paramTypes = getParamTypes(functionRecord, arity, dataverseName);
+        List<TypeSignature> paramTypes = getParamTypes(functionRecord, dataverseName);
 
+        TypeSignature returnType;
         String returnTypeName = ((AString) functionRecord
                 .getValueByPos(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_RETURN_TYPE_FIELD_INDEX)).getStringValue();
-        String returnTypeDataverseNameCanonical = getString(functionRecord, FIELD_NAME_RETURN_TYPE_DATAVERSE_NAME);
-        TypeSignature returnType = getTypeSignature(returnTypeName, returnTypeDataverseNameCanonical, dataverseName);
+        if (returnTypeName.isEmpty()) {
+            returnType = null; // == any
+        } else {
+            String returnTypeDataverseNameCanonical = getString(functionRecord, FIELD_NAME_RETURN_TYPE_DATAVERSE_NAME);
+            returnType = getTypeSignature(returnTypeName, returnTypeDataverseNameCanonical, dataverseName);
+        }
 
         String definition = ((AString) functionRecord
                 .getValueByPos(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_DEFINITION_FIELD_INDEX)).getStringValue();
@@ -191,32 +197,42 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
                 dependencies);
     }
 
-    private List<TypeSignature> getParamTypes(ARecord functionRecord, int arity, DataverseName functionDataverseName) {
-        List<TypeSignature> paramTypes = new ArrayList<>(arity);
+    private List<TypeSignature> getParamTypes(ARecord functionRecord, DataverseName functionDataverseName) {
         ARecordType functionRecordType = functionRecord.getType();
         int paramTypesFieldIdx = functionRecordType.getFieldIndex(FUNCTION_ARECORD_FUNCTION_PARAMTYPES_FIELD_NAME);
-        if (paramTypesFieldIdx >= 0) {
-            IACursor cursor = ((AOrderedList) functionRecord.getValueByPos(paramTypesFieldIdx)).getCursor();
-            while (cursor.next()) {
-                ARecord paramTypeRecord = (ARecord) cursor.get();
-                String paramTypeName = getString(paramTypeRecord, FIELD_NAME_TYPE);
-                String paramTypeDataverseNameCanonical = getString(paramTypeRecord, FIELD_NAME_DATAVERSE_NAME);
-                TypeSignature paramType =
-                        getTypeSignature(paramTypeName, paramTypeDataverseNameCanonical, functionDataverseName);
-                paramTypes.add(paramType);
-            }
-        } else {
-            for (int i = 0; i < arity; i++) {
-                paramTypes.add(TypeUtil.ANY_TYPE_SIGNATURE);
+        if (paramTypesFieldIdx < 0) {
+            return null;
+        }
+
+        AOrderedList paramTypeList = (AOrderedList) functionRecord.getValueByPos(paramTypesFieldIdx);
+        List<TypeSignature> paramTypes = new ArrayList<>(paramTypeList.size());
+        IACursor cursor = paramTypeList.getCursor();
+        while (cursor.next()) {
+            IAObject paramTypeObject = cursor.get();
+            TypeSignature paramType;
+            switch (paramTypeObject.getType().getTypeTag()) {
+                case NULL:
+                    paramType = null; // == any
+                    break;
+                case OBJECT:
+                    ARecord paramTypeRecord = (ARecord) paramTypeObject;
+                    String paramTypeName = getString(paramTypeRecord, FIELD_NAME_TYPE);
+                    String paramTypeDataverseNameCanonical = getString(paramTypeRecord, FIELD_NAME_DATAVERSE_NAME);
+                    paramType = getTypeSignature(paramTypeName, paramTypeDataverseNameCanonical, functionDataverseName);
+                    break;
+                default:
+                    throw new IllegalStateException(); //TODO:FIXME
             }
+            paramTypes.add(paramType);
         }
         return paramTypes;
     }
 
     private TypeSignature getTypeSignature(String typeName, String typeDataverseNameCanonical,
             DataverseName functionDataverseName) {
+        // back-compat: handle "any"
         if (BuiltinType.ANY.getTypeName().equals(typeName)) {
-            return TypeUtil.ANY_TYPE_SIGNATURE;
+            return null; // == any
         }
         BuiltinType builtinType = BuiltinTypeMap.getBuiltinType(typeName);
         if (builtinType != null) {
@@ -335,8 +351,9 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
 
         // write field 4
         // Note: return type's dataverse name is written later in the open part
+        TypeSignature returnType = function.getReturnType();
         fieldValue.reset();
-        aString.setValue(function.getReturnType().getName());
+        aString.setValue(returnType != null ? returnType.getName() : "");
         stringSerde.serialize(aString, fieldValue.getDataOutput());
         recordBuilder.addField(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_RETURN_TYPE_FIELD_INDEX, fieldValue);
 
@@ -429,13 +446,21 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
     }
 
     protected void writeParameterTypes(Function function) throws HyracksDataException {
+        List<TypeSignature> parameterTypes = function.getParameterTypes();
+        if (parameterTypes == null) {
+            return;
+        }
         OrderedListBuilder listBuilder = new OrderedListBuilder();
         ArrayBackedValueStorage itemValue = new ArrayBackedValueStorage();
         listBuilder.reset(DefaultOpenFieldType.NESTED_OPEN_AORDERED_LIST_TYPE);
-        for (TypeSignature paramType : function.getParameterTypes()) {
+        for (TypeSignature paramType : parameterTypes) {
             itemValue.reset();
-            writeTypeRecord(paramType.getDataverseName(), paramType.getName(), function.getDataverseName(),
-                    itemValue.getDataOutput());
+            if (paramType == null) {
+                nullSerde.serialize(ANull.NULL, itemValue.getDataOutput());
+            } else {
+                writeTypeRecord(paramType.getDataverseName(), paramType.getName(), function.getDataverseName(),
+                        itemValue.getDataOutput());
+            }
             listBuilder.addItem(itemValue);
         }
         fieldValue.reset();
@@ -487,18 +512,21 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
     }
 
     protected void writeReturnTypeDataverseName(Function function) throws HyracksDataException {
-        DataverseName returnTypeDataverseName = function.getReturnType().getDataverseName();
-        boolean skipReturnTypeDataverseName =
-                returnTypeDataverseName == null || returnTypeDataverseName.equals(function.getDataverseName());
-        if (!skipReturnTypeDataverseName) {
-            fieldName.reset();
-            aString.setValue(FIELD_NAME_RETURN_TYPE_DATAVERSE_NAME);
-            stringSerde.serialize(aString, fieldName.getDataOutput());
-            fieldValue.reset();
-            aString.setValue(returnTypeDataverseName.getCanonicalForm());
-            stringSerde.serialize(aString, fieldValue.getDataOutput());
-            recordBuilder.addField(fieldName, fieldValue);
+        TypeSignature returnType = function.getReturnType();
+        if (returnType == null) {
+            return;
+        }
+        DataverseName returnTypeDataverseName = returnType.getDataverseName();
+        if (returnTypeDataverseName == null || returnTypeDataverseName.equals(function.getDataverseName())) {
+            return;
         }
+        fieldName.reset();
+        aString.setValue(FIELD_NAME_RETURN_TYPE_DATAVERSE_NAME);
+        stringSerde.serialize(aString, fieldName.getDataOutput());
+        fieldValue.reset();
+        aString.setValue(returnTypeDataverseName.getCanonicalForm());
+        stringSerde.serialize(aString, fieldValue.getDataOutput());
+        recordBuilder.addField(fieldName, fieldValue);
     }
 
     protected void writeNullCall(Function function) throws HyracksDataException {
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalFunctionCompilerUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalFunctionCompilerUtil.java
index f37823c..029b13a 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalFunctionCompilerUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalFunctionCompilerUtil.java
@@ -19,6 +19,7 @@
 package org.apache.asterix.metadata.functions;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.asterix.common.exceptions.AsterixException;
@@ -62,25 +63,24 @@ public class ExternalFunctionCompilerUtil {
 
     private static IFunctionInfo getScalarFunctionInfo(MetadataProvider metadataProvider, Function function)
             throws AlgebricksException {
-        if (function.getDeterministic() == null) {
-            throw new AsterixException(ErrorCode.METADATA_ERROR, "");
-        }
 
-        List<IAType> paramTypes = new ArrayList<>(function.getParameterTypes().size());
-        for (TypeSignature ts : function.getParameterTypes()) {
-            IAType paramType = resolveFunctionType(ts, metadataProvider);
-            paramTypes.add(paramType);
-        }
+        List<IAType> paramTypes = getParameterTypes(function, metadataProvider);
 
-        IAType returnType = resolveFunctionType(function.getReturnType(), metadataProvider);
+        IAType returnType = getType(function.getReturnType(), metadataProvider);
 
         IResultTypeComputer typeComputer = new ExternalTypeComputer(returnType, paramTypes);
 
         ExternalFunctionLanguage lang = getExternalFunctionLanguage(function.getLanguage());
 
+        Boolean deterministic = function.getDeterministic();
+        if (deterministic == null) {
+            // all external functions should store 'deterministic' property
+            throw new AsterixException(ErrorCode.METADATA_ERROR, function.getSignature().toString());
+        }
+
         return new ExternalScalarFunctionInfo(function.getSignature().createFunctionIdentifier(), paramTypes,
                 returnType, typeComputer, lang, function.getLibraryDataverseName(), function.getLibraryName(),
-                function.getExternalIdentifier(), function.getResources(), function.getDeterministic());
+                function.getExternalIdentifier(), function.getResources(), deterministic);
     }
 
     private static IFunctionInfo getUnnestFunctionInfo(MetadataProvider metadataProvider, Function function) {
@@ -95,9 +95,41 @@ public class ExternalFunctionCompilerUtil {
         return null;
     }
 
-    private static IAType resolveFunctionType(TypeSignature typeSignature, MetadataProvider metadataProvider)
+    private static List<IAType> getParameterTypes(Function function, MetadataProvider metadataProvider)
+            throws AlgebricksException {
+        int arity = function.getArity();
+        if (arity == 0) {
+            return Collections.emptyList();
+        } else if (arity >= 0) {
+            List<IAType> types = new ArrayList<>(arity);
+            List<TypeSignature> typeSignatures = function.getParameterTypes();
+            if (typeSignatures != null) {
+                if (typeSignatures.size() != arity) {
+                    throw new AsterixException(ErrorCode.METADATA_ERROR, function.getSignature().toString());
+                }
+                for (TypeSignature ts : typeSignatures) {
+                    IAType paramType = getType(ts, metadataProvider);
+                    types.add(paramType);
+                }
+            } else {
+                for (int i = 0; i < arity; i++) {
+                    types.add(BuiltinType.ANY);
+                }
+            }
+            return types;
+        } else {
+            // we don't yet support variadic external functions
+            throw new AsterixException(ErrorCode.METADATA_ERROR, function.getSignature().toString());
+        }
+    }
+
+    private static IAType getType(TypeSignature typeSignature, MetadataProvider metadataProvider)
             throws AlgebricksException {
+        if (typeSignature == null) {
+            return BuiltinType.ANY;
+        }
         String typeName = typeSignature.getName();
+        // back-compat: handle "any"
         if (BuiltinType.ANY.getTypeName().equals(typeName)) {
             return BuiltinType.ANY;
         }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
index 11dc1ee..7660909 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
@@ -19,6 +19,8 @@
 package org.apache.asterix.metadata.utils;
 
 import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -28,12 +30,12 @@ import org.apache.asterix.common.exceptions.AsterixException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.metadata.entities.Dataset;
+import org.apache.asterix.metadata.entities.Function;
 import org.apache.asterix.metadata.entities.Index;
 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;
-import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.om.types.TypeSignature;
 import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
@@ -46,8 +48,6 @@ import org.apache.hyracks.algebricks.common.utils.Pair;
  */
 public class TypeUtil {
 
-    public static final TypeSignature ANY_TYPE_SIGNATURE = new TypeSignature(BuiltinType.ANY);
-
     private static final char TYPE_NAME_DELIMITER = '$';
 
     private static final String DATASET_INLINE_TYPE_PREFIX = "$d$t$";
@@ -263,7 +263,12 @@ public class TypeUtil {
     }
 
     public static boolean isDatasetInlineTypeName(Dataset dataset, DataverseName typeDataverseName, String typeName) {
-        return dataset.getDataverseName().equals(typeDataverseName) && typeName.startsWith(DATASET_INLINE_TYPE_PREFIX);
+        return isInlineTypeName(dataset.getDataverseName(), typeDataverseName, typeName, DATASET_INLINE_TYPE_PREFIX);
+    }
+
+    private static boolean isInlineTypeName(DataverseName entityDataverseName, DataverseName typeDataverseName,
+            String typeName, String inlineTypePrefix) {
+        return entityDataverseName.equals(typeDataverseName) && typeName.startsWith(inlineTypePrefix);
     }
 
     public static String createFunctionParameterTypeName(String functionName, int arity, int parameterIndex) {
@@ -277,4 +282,39 @@ public class TypeUtil {
 
         return sb.toString();
     }
+
+    public static boolean isFunctionInlineTypeName(Function function, DataverseName typeDataverseName,
+            String typeName) {
+        return isInlineTypeName(function.getDataverseName(), typeDataverseName, typeName, FUNCTION_INLINE_TYPE_PREFIX);
+    }
+
+    public static String getFullyQualifiedDisplayName(DataverseName dataverseName, String typeName) {
+        return dataverseName + "." + typeName;
+    }
+
+    /**
+     * Inline type names are unique within a function, so we don't need to perform duplicate elimination
+     */
+    public static List<TypeSignature> getFunctionInlineTypes(Function function) {
+        List<TypeSignature> inlineTypes = Collections.emptyList();
+        TypeSignature returnType = function.getReturnType();
+        if (returnType != null
+                && isFunctionInlineTypeName(function, returnType.getDataverseName(), returnType.getName())) {
+            inlineTypes = new ArrayList<>();
+            inlineTypes.add(returnType);
+        }
+        List<TypeSignature> parameterTypes = function.getParameterTypes();
+        if (parameterTypes != null) {
+            for (TypeSignature parameterType : parameterTypes) {
+                if (parameterType != null && isFunctionInlineTypeName(function, parameterType.getDataverseName(),
+                        parameterType.getName())) {
+                    if (inlineTypes.isEmpty()) {
+                        inlineTypes = new ArrayList<>();
+                    }
+                    inlineTypes.add(parameterType);
+                }
+            }
+        }
+        return inlineTypes;
+    }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ExpressionTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ExpressionTypeComputer.java
index 1dcf6a6..e581140 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ExpressionTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ExpressionTypeComputer.java
@@ -20,10 +20,8 @@ package org.apache.asterix.dataflow.data.common;
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
-import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.om.constants.AsterixConstantValue;
-import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.ExternalFunctionInfo;
+import org.apache.asterix.om.functions.FunctionInfo;
 import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
@@ -35,7 +33,6 @@ import org.apache.hyracks.algebricks.core.algebra.expressions.IAlgebricksConstan
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionTypeComputer;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
 import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
 
 public class ExpressionTypeComputer implements IExpressionTypeComputer {
@@ -69,18 +66,7 @@ public class ExpressionTypeComputer implements IExpressionTypeComputer {
 
     private IAType getTypeForFunction(AbstractFunctionCallExpression expr, IVariableTypeEnvironment env,
             IMetadataProvider<?, ?> mp) throws AlgebricksException {
-        FunctionIdentifier fi = expr.getFunctionIdentifier();
-        // Note: built-in functions + udfs
-        IResultTypeComputer rtc;
-        FunctionSignature signature = new FunctionSignature(fi);
-        if (BuiltinFunctions.isBuiltinCompilerFunction(signature, true)) {
-            rtc = BuiltinFunctions.getResultTypeComputer(fi);
-        } else {
-            rtc = ((ExternalFunctionInfo) expr.getFunctionInfo()).getResultTypeComputer();
-        }
-        if (rtc == null) {
-            throw new AlgebricksException("Type computer missing for " + fi);
-        }
+        IResultTypeComputer rtc = ((FunctionInfo) expr.getFunctionInfo()).getResultTypeComputer();
         return rtc.computeType(expr, env, mp);
     }
 
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/PartialAggregationTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/PartialAggregationTypeComputer.java
index 4a0384d..f012f5c 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/PartialAggregationTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/PartialAggregationTypeComputer.java
@@ -19,6 +19,7 @@
 package org.apache.asterix.dataflow.data.common;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.FunctionInfo;
 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;
@@ -45,7 +46,6 @@ public class PartialAggregationTypeComputer implements IPartialAggregationTypeCo
 
     private Object getTypeForFunction(AbstractFunctionCallExpression expr, IVariableTypeEnvironment env,
             IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
-        return BuiltinFunctions.getResultTypeComputer(expr.getFunctionIdentifier()).computeType(expr, env,
-                metadataProvider);
+        return ((FunctionInfo) expr.getFunctionInfo()).getResultTypeComputer().computeType(expr, env, metadataProvider);
     }
 }
diff --git a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctionInfo.java
similarity index 55%
copy from asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
copy to asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctionInfo.java
index 52d7d19..acb348d 100644
--- a/asterixdb/asterix-app/src/test/resources/metadata/queries/basic/meta13/meta13.1.ddl.aql
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctionInfo.java
@@ -16,19 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*
- * Description  : Create functions and drop that function and query metadata 
- *              : to verify entries in Function dataset for the dropped UDF.
- * Expected Res : Success
- * Date         : Sep 17 2012
- */
 
-drop dataverse test if exists;
-create dataverse test;
+package org.apache.asterix.om.functions;
 
-create function test.foo(){
-"drop this function"
-}
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 
-drop function test.foo@0;
+public final class BuiltinFunctionInfo extends FunctionInfo {
+    private static final long serialVersionUID = -6013109889177637590L;
 
+    private final boolean isPrivate;
+
+    public BuiltinFunctionInfo(FunctionIdentifier fi, IResultTypeComputer typeComputer, boolean isFunctional,
+            boolean isPrivate) {
+        super(fi, typeComputer, isFunctional);
+        this.isPrivate = isPrivate;
+    }
+
+    public boolean isPrivate() {
+        return isPrivate;
+    }
+}
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 96ad4a9..f74e730 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
@@ -34,7 +34,6 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.asterix.common.functions.FunctionConstants;
-import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.ABinaryTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.ABooleanArrayContainsTypeComputer;
@@ -136,7 +135,6 @@ import org.apache.asterix.om.typecomputer.impl.TreatAsTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.UnaryBinaryInt64TypeComputer;
 import org.apache.asterix.om.typecomputer.impl.UniformInputTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.UnorderedListConstructorTypeComputer;
-import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
@@ -149,34 +147,27 @@ import org.apache.hyracks.algebricks.core.algebra.properties.UnpartitionedProper
 
 public class BuiltinFunctions {
 
-    public enum SpatialFilterKind {
-        SI
-    }
-
-    private static final FunctionInfoRepository registeredFunctions = new FunctionInfoRepository();
-    private static final Map<IFunctionInfo, ATypeHierarchy.Domain> registeredFunctionsDomain = new HashMap<>();
+    private static final Map<FunctionIdentifier, BuiltinFunctionInfo> registeredFunctions = new HashMap<>();
+    private static final Map<FunctionIdentifier, Set<? extends BuiltinFunctionProperty>> builtinFunctionProperties =
+            new HashMap<>();
 
-    // it is supposed to be an identity mapping
-    private static final Map<IFunctionInfo, IFunctionInfo> builtinPublicFunctionsSet = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> builtinPrivateFunctionsSet = new HashMap<>();
-    private static final Map<IFunctionInfo, IResultTypeComputer> funTypeComputer = new HashMap<>();
-    private static final Map<IFunctionInfo, Set<? extends BuiltinFunctionProperty>> builtinFunctionProperties =
+    private static final Map<FunctionIdentifier, IFunctionToDataSourceRewriter> datasourceFunctions = new HashMap<>();
+    private static final Map<FunctionIdentifier, Boolean> builtinUnnestingFunctions = new HashMap<>();
+
+    private static final Set<FunctionIdentifier> builtinAggregateFunctions = new HashSet<>();
+    private static final Set<FunctionIdentifier> globalAggregateFunctions = new HashSet<>();
+    private static final Map<FunctionIdentifier, FunctionIdentifier> aggregateToLocalAggregate = new HashMap<>();
+    private static final Map<FunctionIdentifier, FunctionIdentifier> aggregateToIntermediateAggregate = new HashMap<>();
+    private static final Map<FunctionIdentifier, FunctionIdentifier> aggregateToGlobalAggregate = new HashMap<>();
+    private static final Map<FunctionIdentifier, FunctionIdentifier> aggregateToSerializableAggregate = new HashMap<>();
+    private static final Map<FunctionIdentifier, FunctionIdentifier> scalarToAggregateFunctionMap = new HashMap<>();
+    private static final Map<FunctionIdentifier, FunctionIdentifier> distinctToRegularAggregateFunctionMap =
             new HashMap<>();
-    private static final Set<IFunctionInfo> builtinAggregateFunctions = new HashSet<>();
-    private static final Map<IFunctionInfo, IFunctionToDataSourceRewriter> datasourceFunctions = new HashMap<>();
-    private static final Set<IFunctionInfo> similarityFunctions = new HashSet<>();
-    private static final Set<IFunctionInfo> globalAggregateFunctions = new HashSet<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> aggregateToLocalAggregate = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> aggregateToIntermediateAggregate = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> aggregateToGlobalAggregate = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> aggregateToSerializableAggregate = new HashMap<>();
-    private static final Map<IFunctionInfo, Boolean> builtinUnnestingFunctions = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> scalarToAggregateFunctionMap = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> distinctToRegularAggregateFunctionMap = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> sqlToWindowFunctions = new HashMap<>();
-    private static final Set<IFunctionInfo> windowFunctions = new HashSet<>();
-
-    private static final Map<IFunctionInfo, SpatialFilterKind> spatialFilterFunctions = new HashMap<>();
+    private static final Map<FunctionIdentifier, FunctionIdentifier> sqlToWindowFunctions = new HashMap<>();
+    private static final Set<FunctionIdentifier> windowFunctions = new HashSet<>();
+
+    private static final Map<FunctionIdentifier, SpatialFilterKind> spatialFilterFunctions = new HashMap<>();
+    private static final Set<FunctionIdentifier> similarityFunctions = new HashSet<>();
 
     public static final FunctionIdentifier TYPE_OF = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "type-of", 1);
     public static final FunctionIdentifier GET_HANDLE =
@@ -1602,16 +1593,7 @@ public class BuiltinFunctions {
     public static final FunctionIdentifier DECODE_DATAVERSE_NAME =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "decode-dataverse-name", 1);
 
-    public static IFunctionInfo getAsterixFunctionInfo(FunctionIdentifier fid) {
-        return registeredFunctions.get(fid);
-    }
-
-    public static FunctionInfo lookupFunction(FunctionIdentifier fid) {
-        return (FunctionInfo) registeredFunctions.get(fid);
-    }
-
     static {
-
         // first, take care of Algebricks builtin functions
         addFunction(IS_MISSING, BooleanOnlyTypeComputer.INSTANCE, true);
         addFunction(IS_UNKNOWN, BooleanOnlyTypeComputer.INSTANCE, true);
@@ -1692,9 +1674,9 @@ public class BuiltinFunctions {
 
         addFunction(FLOAT_CONSTRUCTOR, AFloatTypeComputer.INSTANCE, true);
         addPrivateFunction(FUZZY_EQ, BooleanFunctionTypeComputer.INSTANCE, true);
-        addPrivateFunction(GET_HANDLE, null, true);
+        addPrivateFunction(GET_HANDLE, AnyTypeComputer.INSTANCE, true);
         addPrivateFunction(GET_ITEM, NonTaggedGetItemResultType.INSTANCE, true);
-        addPrivateFunction(GET_DATA, null, true);
+        addPrivateFunction(GET_DATA, AnyTypeComputer.INSTANCE, true);
         addPrivateFunction(GRAM_TOKENS, OrderedListOfAStringTypeComputer.INSTANCE, true);
         addPrivateFunction(HASHED_GRAM_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE, true);
         addPrivateFunction(HASHED_WORD_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE, true);
@@ -1705,8 +1687,8 @@ public class BuiltinFunctions {
         addFunction(INT64_CONSTRUCTOR, AInt64TypeComputer.INSTANCE, true);
         addFunction(LEN, AInt64TypeComputer.INSTANCE, true);
         addFunction(LINE_CONSTRUCTOR, ALineTypeComputer.INSTANCE, true);
-        addPrivateFunction(MAKE_FIELD_INDEX_HANDLE, null, true);
-        addPrivateFunction(MAKE_FIELD_NAME_HANDLE, null, true);
+        addPrivateFunction(MAKE_FIELD_INDEX_HANDLE, AnyTypeComputer.INSTANCE, true);
+        addPrivateFunction(MAKE_FIELD_NAME_HANDLE, AnyTypeComputer.INSTANCE, true);
 
         addPrivateFunction(NUMERIC_UNARY_MINUS, NumericUnaryTypeComputer.INSTANCE, true);
         addPrivateFunction(NUMERIC_SUBTRACT, NumericAddSubMulDivTypeComputer.INSTANCE_SUB, true);
@@ -2146,7 +2128,7 @@ public class BuiltinFunctions {
         addFunction(SPATIAL_AREA, ADoubleTypeComputer.INSTANCE, true);
         addFunction(SPATIAL_CELL, ARectangleTypeComputer.INSTANCE, true);
         addFunction(SPATIAL_DISTANCE, ADoubleTypeComputer.INSTANCE, true);
-        addFunctionWithDomain(SPATIAL_INTERSECT, ATypeHierarchy.Domain.SPATIAL, ABooleanTypeComputer.INSTANCE, true);
+        addFunction(SPATIAL_INTERSECT, ABooleanTypeComputer.INSTANCE, true);
         addFunction(GET_POINT_X_COORDINATE_ACCESSOR, ADoubleTypeComputer.INSTANCE, true);
         addFunction(GET_POINT_Y_COORDINATE_ACCESSOR, ADoubleTypeComputer.INSTANCE, true);
         addFunction(GET_CIRCLE_RADIUS_ACCESSOR, ADoubleTypeComputer.INSTANCE, true);
@@ -2235,7 +2217,7 @@ public class BuiltinFunctions {
 
         addFunction(TID, AInt64TypeComputer.INSTANCE, true);
         addFunction(TIME_CONSTRUCTOR, ATimeTypeComputer.INSTANCE, true);
-        addPrivateFunction(TYPE_OF, null, true);
+        addPrivateFunction(TYPE_OF, AnyTypeComputer.INSTANCE, true);
         addPrivateFunction(UNORDERED_LIST_CONSTRUCTOR, UnorderedListConstructorTypeComputer.INSTANCE, true);
         addFunction(WORD_TOKENS, OrderedListOfAStringTypeComputer.INSTANCE, true);
 
@@ -3084,77 +3066,65 @@ public class BuiltinFunctions {
 
     public static void addDatasourceFunction(FunctionIdentifier fi, IFunctionToDataSourceRewriter transformer,
             DataSourceFunctionProperty... properties) {
-        IFunctionInfo finfo = getAsterixFunctionInfo(fi);
-        datasourceFunctions.put(finfo, transformer);
-        registerFunctionProperties(finfo, DataSourceFunctionProperty.class, properties);
+        datasourceFunctions.put(fi, transformer);
+        registerFunctionProperties(fi, DataSourceFunctionProperty.class, properties);
     }
 
     public static IFunctionToDataSourceRewriter getDatasourceTransformer(FunctionIdentifier fi) {
-        return datasourceFunctions.getOrDefault(getAsterixFunctionInfo(fi), IFunctionToDataSourceRewriter.NOOP);
+        return datasourceFunctions.get(fi);
     }
 
-    public static boolean isBuiltinCompilerFunction(FunctionSignature signature, boolean includePrivateFunctions) {
-        return getBuiltinCompilerFunction(signature.getName(), signature.getArity(), includePrivateFunctions) != null;
+    public static BuiltinFunctionInfo getBuiltinFunctionInfo(FunctionIdentifier fi) {
+        return registeredFunctions.get(fi);
     }
 
-    public static FunctionIdentifier getBuiltinCompilerFunction(String name, int arity,
-            boolean includePrivateFunctions) {
-        FunctionIdentifier fi = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, name, arity);
-        IFunctionInfo finfo = getAsterixFunctionInfo(fi);
-        if (builtinPublicFunctionsSet.containsKey(finfo)
-                || (includePrivateFunctions && builtinPrivateFunctionsSet.containsKey(finfo))) {
-            return fi;
+    public static BuiltinFunctionInfo resolveBuiltinFunction(String name, int arity) {
+        //TODO:optimize
+        BuiltinFunctionInfo finfo;
+        finfo = getBuiltinFunctionInfo(new FunctionIdentifier(FunctionConstants.ASTERIX_NS, name, arity));
+        if (finfo != null) {
+            return finfo;
+        }
+        finfo = getBuiltinFunctionInfo(
+                new FunctionIdentifier(FunctionConstants.ASTERIX_NS, name, FunctionIdentifier.VARARGS));
+        if (finfo != null) {
+            return finfo;
         }
-        fi = new FunctionIdentifier(AlgebricksBuiltinFunctions.ALGEBRICKS_NS, name, arity);
-        finfo = getAsterixFunctionInfo(fi);
-        return builtinPublicFunctionsSet.containsKey(finfo)
-                || (includePrivateFunctions && builtinPrivateFunctionsSet.containsKey(finfo)) ? fi : null;
+        finfo = getBuiltinFunctionInfo(new FunctionIdentifier(AlgebricksBuiltinFunctions.ALGEBRICKS_NS, name, arity));
+        if (finfo != null) {
+            return finfo;
+        }
+        return getBuiltinFunctionInfo(
+                new FunctionIdentifier(AlgebricksBuiltinFunctions.ALGEBRICKS_NS, name, FunctionIdentifier.VARARGS));
     }
 
     public static boolean isBuiltinAggregateFunction(FunctionIdentifier fi) {
-        return builtinAggregateFunctions.contains(getAsterixFunctionInfo(fi));
+        return builtinAggregateFunctions.contains(fi);
     }
 
     public static boolean isBuiltinUnnestingFunction(FunctionIdentifier fi) {
-        return builtinUnnestingFunctions.get(getAsterixFunctionInfo(fi)) != null;
+        return builtinUnnestingFunctions.containsKey(fi);
     }
 
     public static boolean returnsUniqueValues(FunctionIdentifier fi) {
-        Boolean ruv = builtinUnnestingFunctions.get(getAsterixFunctionInfo(fi));
-        return ruv != null && ruv.booleanValue();
-    }
-
-    public static FunctionIdentifier getLocalAggregateFunction(FunctionIdentifier fi) {
-        return aggregateToLocalAggregate.get(getAsterixFunctionInfo(fi)).getFunctionIdentifier();
-    }
-
-    public static FunctionIdentifier getGlobalAggregateFunction(FunctionIdentifier fi) {
-        return aggregateToGlobalAggregate.get(getAsterixFunctionInfo(fi)).getFunctionIdentifier();
+        Boolean ruv = builtinUnnestingFunctions.get(fi);
+        return ruv != null && ruv;
     }
 
     public static FunctionIdentifier getIntermediateAggregateFunction(FunctionIdentifier fi) {
-        IFunctionInfo funcInfo = aggregateToIntermediateAggregate.get(getAsterixFunctionInfo(fi));
-        if (funcInfo == null) {
-            return null;
-        }
-        return funcInfo.getFunctionIdentifier();
-    }
-
-    public static FunctionIdentifier getBuiltinFunctionIdentifier(FunctionIdentifier fi) {
-        IFunctionInfo finfo = getAsterixFunctionInfo(fi);
-        return finfo == null ? null : finfo.getFunctionIdentifier();
+        return aggregateToIntermediateAggregate.get(fi);
     }
 
     public static AggregateFunctionCallExpression makeAggregateFunctionExpression(FunctionIdentifier fi,
             List<Mutable<ILogicalExpression>> args) {
-        IFunctionInfo finfo = getAsterixFunctionInfo(fi);
-        IFunctionInfo fiLocal = aggregateToLocalAggregate.get(finfo);
-        IFunctionInfo fiGlobal = aggregateToGlobalAggregate.get(finfo);
+        IFunctionInfo finfo = getBuiltinFunctionInfo(fi);
+        FunctionIdentifier fiLocal = aggregateToLocalAggregate.get(fi);
+        FunctionIdentifier fiGlobal = aggregateToGlobalAggregate.get(fi);
 
         if (fiLocal != null && fiGlobal != null) {
             AggregateFunctionCallExpression fun = new AggregateFunctionCallExpression(finfo, true, args);
-            fun.setStepTwoAggregate(fiGlobal);
-            fun.setStepOneAggregate(fiLocal);
+            fun.setStepTwoAggregate(getBuiltinFunctionInfo(fiGlobal));
+            fun.setStepOneAggregate(getBuiltinFunctionInfo(fiLocal));
             return fun;
         } else {
             return new AggregateFunctionCallExpression(finfo, false, args);
@@ -3162,166 +3132,151 @@ public class BuiltinFunctions {
     }
 
     public static boolean isAggregateFunctionSerializable(FunctionIdentifier fi) {
-        IFunctionInfo finfo = getAsterixFunctionInfo(fi);
-        return aggregateToSerializableAggregate.get(finfo) != null;
+        return aggregateToSerializableAggregate.containsKey(fi);
     }
 
     public static AggregateFunctionCallExpression makeSerializableAggregateFunctionExpression(FunctionIdentifier fi,
             List<Mutable<ILogicalExpression>> args) {
 
-        IFunctionInfo finfo = getAsterixFunctionInfo(fi);
-        IFunctionInfo serializableFinfo = aggregateToSerializableAggregate.get(finfo);
-        if (serializableFinfo == null) {
-            throw new IllegalStateException(
-                    "no serializable implementation for aggregate function " + serializableFinfo);
+        FunctionIdentifier serializableFi = aggregateToSerializableAggregate.get(fi);
+        if (serializableFi == null) {
+            throw new IllegalStateException("no serializable implementation for aggregate function " + fi);
         }
+        IFunctionInfo serializableFinfo = getBuiltinFunctionInfo(serializableFi);
 
-        IFunctionInfo fiLocal = aggregateToLocalAggregate.get(serializableFinfo);
-        IFunctionInfo fiGlobal = aggregateToGlobalAggregate.get(serializableFinfo);
+        FunctionIdentifier fiLocal = aggregateToLocalAggregate.get(serializableFi);
+        FunctionIdentifier fiGlobal = aggregateToGlobalAggregate.get(serializableFi);
 
         if (fiLocal != null && fiGlobal != null) {
             AggregateFunctionCallExpression fun = new AggregateFunctionCallExpression(serializableFinfo, true, args);
-            fun.setStepTwoAggregate(fiGlobal);
-            fun.setStepOneAggregate(fiLocal);
+            fun.setStepTwoAggregate(getBuiltinFunctionInfo(fiGlobal));
+            fun.setStepOneAggregate(getBuiltinFunctionInfo(fiLocal));
             return fun;
         } else {
             return new AggregateFunctionCallExpression(serializableFinfo, false, args);
         }
     }
 
-    public static IResultTypeComputer getResultTypeComputer(FunctionIdentifier fi) {
-        return funTypeComputer.get(getAsterixFunctionInfo(fi));
-    }
-
     public static FunctionIdentifier getAggregateFunction(FunctionIdentifier scalarVersionOfAggregate) {
-        IFunctionInfo finfo = scalarToAggregateFunctionMap.get(getAsterixFunctionInfo(scalarVersionOfAggregate));
-        return finfo == null ? null : finfo.getFunctionIdentifier();
+        return scalarToAggregateFunctionMap.get(scalarVersionOfAggregate);
     }
 
     public static FunctionIdentifier getAggregateFunctionForDistinct(FunctionIdentifier distinctVersionOfAggregate) {
-        IFunctionInfo finfo =
-                distinctToRegularAggregateFunctionMap.get(getAsterixFunctionInfo(distinctVersionOfAggregate));
-        return finfo == null ? null : finfo.getFunctionIdentifier();
+        return distinctToRegularAggregateFunctionMap.get(distinctVersionOfAggregate);
     }
 
     public static void addFunction(FunctionIdentifier fi, IResultTypeComputer typeComputer, boolean isFunctional) {
-        addFunctionWithDomain(fi, ATypeHierarchy.Domain.ANY, typeComputer, isFunctional);
-    }
-
-    public static void addFunctionWithDomain(FunctionIdentifier fi, ATypeHierarchy.Domain funcDomain,
-            IResultTypeComputer typeComputer, boolean isFunctional) {
-        IFunctionInfo functionInfo = new FunctionInfo(fi, isFunctional);
-        builtinPublicFunctionsSet.put(functionInfo, functionInfo);
-        funTypeComputer.put(functionInfo, typeComputer);
-        registeredFunctions.put(fi, functionInfo);
-        registeredFunctionsDomain.put(functionInfo, funcDomain);
+        addFunction(new BuiltinFunctionInfo(fi, typeComputer, isFunctional, false));
     }
 
     public static void addPrivateFunction(FunctionIdentifier fi, IResultTypeComputer typeComputer,
             boolean isFunctional) {
-        IFunctionInfo functionInfo = new FunctionInfo(fi, isFunctional);
-        builtinPrivateFunctionsSet.put(functionInfo, functionInfo);
-        funTypeComputer.put(functionInfo, typeComputer);
-        registeredFunctions.put(fi, functionInfo);
+        addFunction(new BuiltinFunctionInfo(fi, typeComputer, isFunctional, true));
+    }
+
+    private static void addFunction(BuiltinFunctionInfo functionInfo) {
+        registeredFunctions.put(functionInfo.getFunctionIdentifier(), functionInfo);
     }
 
-    private static <T extends Enum<T> & BuiltinFunctionProperty> void registerFunctionProperties(IFunctionInfo finfo,
+    private static <T extends Enum<T> & BuiltinFunctionProperty> void registerFunctionProperties(FunctionIdentifier fid,
             Class<T> propertyClass, T[] properties) {
         if (properties == null) {
             return;
         }
         Set<T> propertySet = EnumSet.noneOf(propertyClass);
         Collections.addAll(propertySet, properties);
-        builtinFunctionProperties.put(finfo, propertySet);
+        builtinFunctionProperties.put(fid, propertySet);
     }
 
     public static boolean builtinFunctionHasProperty(FunctionIdentifier fi, BuiltinFunctionProperty property) {
-        Set<? extends BuiltinFunctionProperty> propertySet = builtinFunctionProperties.get(getAsterixFunctionInfo(fi));
+        Set<? extends BuiltinFunctionProperty> propertySet = builtinFunctionProperties.get(fi);
         return propertySet != null && propertySet.contains(property);
     }
 
     public static void addAgg(FunctionIdentifier fi) {
-        builtinAggregateFunctions.add(getAsterixFunctionInfo(fi));
+        builtinAggregateFunctions.add(fi);
     }
 
     public static void addLocalAgg(FunctionIdentifier fi, FunctionIdentifier localfi) {
-        aggregateToLocalAggregate.put(getAsterixFunctionInfo(fi), getAsterixFunctionInfo(localfi));
+        aggregateToLocalAggregate.put(fi, localfi);
     }
 
     public static void addIntermediateAgg(FunctionIdentifier fi, FunctionIdentifier globalfi) {
-        aggregateToIntermediateAggregate.put(getAsterixFunctionInfo(fi), getAsterixFunctionInfo(globalfi));
+        aggregateToIntermediateAggregate.put(fi, globalfi);
     }
 
     public static void addGlobalAgg(FunctionIdentifier fi, FunctionIdentifier globalfi) {
-        aggregateToGlobalAggregate.put(getAsterixFunctionInfo(fi), getAsterixFunctionInfo(globalfi));
-        globalAggregateFunctions.add(getAsterixFunctionInfo(globalfi));
+        aggregateToGlobalAggregate.put(fi, globalfi);
+        globalAggregateFunctions.add(globalfi);
     }
 
     public static void addUnnestFun(FunctionIdentifier fi, boolean returnsUniqueValues) {
-        builtinUnnestingFunctions.put(getAsterixFunctionInfo(fi), returnsUniqueValues);
+        builtinUnnestingFunctions.put(fi, returnsUniqueValues);
     }
 
     public static void addSerialAgg(FunctionIdentifier fi, FunctionIdentifier serialfi) {
-        aggregateToSerializableAggregate.put(getAsterixFunctionInfo(fi), getAsterixFunctionInfo(serialfi));
+        aggregateToSerializableAggregate.put(fi, serialfi);
     }
 
     public static void addScalarAgg(FunctionIdentifier fi, FunctionIdentifier scalarfi) {
-        scalarToAggregateFunctionMap.put(getAsterixFunctionInfo(scalarfi), getAsterixFunctionInfo(fi));
+        scalarToAggregateFunctionMap.put(scalarfi, fi);
     }
 
     public static void addDistinctAgg(FunctionIdentifier distinctfi, FunctionIdentifier fi) {
-        distinctToRegularAggregateFunctionMap.put(getAsterixFunctionInfo(distinctfi), getAsterixFunctionInfo(fi));
+        distinctToRegularAggregateFunctionMap.put(distinctfi, fi);
     }
 
     public static void addWindowFunction(FunctionIdentifier sqlfi, FunctionIdentifier winfi,
             WindowFunctionProperty... properties) {
-        IFunctionInfo wininfo = getAsterixFunctionInfo(winfi);
         if (sqlfi != null) {
-            sqlToWindowFunctions.put(getAsterixFunctionInfo(sqlfi), wininfo);
+            sqlToWindowFunctions.put(sqlfi, winfi);
         }
-        windowFunctions.add(wininfo);
-        registerFunctionProperties(wininfo, WindowFunctionProperty.class, properties);
+        windowFunctions.add(winfi);
+        registerFunctionProperties(winfi, WindowFunctionProperty.class, properties);
     }
 
     public static FunctionIdentifier getWindowFunction(FunctionIdentifier sqlfi) {
-        IFunctionInfo finfo = sqlToWindowFunctions.get(getAsterixFunctionInfo(sqlfi));
-        return finfo == null ? null : finfo.getFunctionIdentifier();
+        return sqlToWindowFunctions.get(sqlfi);
     }
 
     public static boolean isWindowFunction(FunctionIdentifier winfi) {
-        return windowFunctions.contains(getAsterixFunctionInfo(winfi));
+        return windowFunctions.contains(winfi);
     }
 
     public static AbstractFunctionCallExpression makeWindowFunctionExpression(FunctionIdentifier winfi,
             List<Mutable<ILogicalExpression>> args) {
-        IFunctionInfo finfo = getAsterixFunctionInfo(winfi);
+        IFunctionInfo finfo = getBuiltinFunctionInfo(winfi);
         if (finfo == null) {
-            throw new IllegalStateException("no implementation for window function " + finfo);
+            throw new IllegalStateException("no implementation for window function " + winfi);
         }
         return new StatefulFunctionCallExpression(finfo, UnpartitionedPropertyComputer.INSTANCE, args);
     }
 
+    public enum SpatialFilterKind {
+        SI
+    }
+
     static {
-        spatialFilterFunctions.put(getAsterixFunctionInfo(BuiltinFunctions.SPATIAL_INTERSECT), SpatialFilterKind.SI);
+        spatialFilterFunctions.put(BuiltinFunctions.SPATIAL_INTERSECT, SpatialFilterKind.SI);
     }
 
     public static boolean isGlobalAggregateFunction(FunctionIdentifier fi) {
-        return globalAggregateFunctions.contains(getAsterixFunctionInfo(fi));
+        return globalAggregateFunctions.contains(fi);
     }
 
     public static boolean isSpatialFilterFunction(FunctionIdentifier fi) {
-        return spatialFilterFunctions.get(getAsterixFunctionInfo(fi)) != null;
+        return spatialFilterFunctions.get(fi) != null;
     }
 
     static {
-        similarityFunctions.add(getAsterixFunctionInfo(SIMILARITY_JACCARD));
-        similarityFunctions.add(getAsterixFunctionInfo(SIMILARITY_JACCARD_CHECK));
-        similarityFunctions.add(getAsterixFunctionInfo(EDIT_DISTANCE));
-        similarityFunctions.add(getAsterixFunctionInfo(EDIT_DISTANCE_CHECK));
-        similarityFunctions.add(getAsterixFunctionInfo(EDIT_DISTANCE_CONTAINS));
+        similarityFunctions.add(SIMILARITY_JACCARD);
+        similarityFunctions.add(SIMILARITY_JACCARD_CHECK);
+        similarityFunctions.add(EDIT_DISTANCE);
+        similarityFunctions.add(EDIT_DISTANCE_CHECK);
+        similarityFunctions.add(EDIT_DISTANCE_CONTAINS);
     }
 
     public static boolean isSimilarityFunction(FunctionIdentifier fi) {
-        return similarityFunctions.contains(getAsterixFunctionInfo(fi));
+        return similarityFunctions.contains(fi);
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/ExternalFunctionInfo.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/ExternalFunctionInfo.java
index 8707018..7477330 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/ExternalFunctionInfo.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/ExternalFunctionInfo.java
@@ -30,12 +30,11 @@ import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 
 public class ExternalFunctionInfo extends FunctionInfo implements IExternalFunctionInfo {
 
-    private static final long serialVersionUID = 3L;
+    private static final long serialVersionUID = 4L;
 
     private final FunctionKind kind;
     private final List<IAType> parameterTypes;
     private final IAType returnType;
-    private final transient IResultTypeComputer rtc;
     private final ExternalFunctionLanguage language;
     private final DataverseName libraryDataverseName;
     private final String libraryName;
@@ -46,11 +45,10 @@ public class ExternalFunctionInfo extends FunctionInfo implements IExternalFunct
             IAType returnType, IResultTypeComputer rtc, ExternalFunctionLanguage language,
             DataverseName libraryDataverseName, String libraryName, List<String> externalIdentifier,
             Map<String, String> resources, boolean deterministic) {
-        super(fid, deterministic);
+        super(fid, rtc, deterministic);
         this.kind = kind;
         this.parameterTypes = parameterTypes;
         this.returnType = returnType;
-        this.rtc = rtc;
         this.language = language;
         this.libraryDataverseName = libraryDataverseName;
         this.libraryName = libraryName;
@@ -73,10 +71,6 @@ public class ExternalFunctionInfo extends FunctionInfo implements IExternalFunct
         return returnType;
     }
 
-    public IResultTypeComputer getResultTypeComputer() {
-        return rtc;
-    }
-
     @Override
     public ExternalFunctionLanguage getLanguage() {
         return language;
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/FunctionInfo.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/FunctionInfo.java
index a7377f8..f7f46f5 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/FunctionInfo.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/FunctionInfo.java
@@ -21,24 +21,23 @@ package org.apache.asterix.om.functions;
 import java.util.List;
 import java.util.Objects;
 
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
 
-public class FunctionInfo implements IFunctionInfo {
-    private static final long serialVersionUID = 5460606629941107898L;
+public abstract class FunctionInfo implements IFunctionInfo {
+    private static final long serialVersionUID = 5460606629941107899L;
 
     private final FunctionIdentifier functionIdentifier;
+    private final transient IResultTypeComputer typeComputer;
     private final boolean isFunctional;
 
-    public FunctionInfo(String namespace, String name, int arity, boolean isFunctional) {
-        this(new FunctionIdentifier(namespace, name, arity), isFunctional);
-    }
-
-    public FunctionInfo(FunctionIdentifier functionIdentifier, boolean isFunctional) {
+    public FunctionInfo(FunctionIdentifier functionIdentifier, IResultTypeComputer typeComputer, boolean isFunctional) {
+        this.functionIdentifier = Objects.requireNonNull(functionIdentifier);
+        this.typeComputer = Objects.requireNonNull(typeComputer);
         this.isFunctional = isFunctional;
-        this.functionIdentifier = functionIdentifier;
     }
 
     @Override
@@ -51,6 +50,10 @@ public class FunctionInfo implements IFunctionInfo {
         return isFunctional;
     }
 
+    public IResultTypeComputer getResultTypeComputer() {
+        return typeComputer;
+    }
+
     /**
      * @param args,
      *            the arguments.
@@ -62,25 +65,7 @@ public class FunctionInfo implements IFunctionInfo {
     }
 
     @Override
-    public int hashCode() {
-        return Objects.hash(functionIdentifier.getNamespace(), functionIdentifier.getName(),
-                functionIdentifier.getArity());
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof FunctionInfo)) {
-            return false;
-        }
-        FunctionInfo info = (FunctionInfo) o;
-        return functionIdentifier.equals(info.getFunctionIdentifier())
-                && functionIdentifier.getArity() == info.getFunctionIdentifier().getArity();
-    }
-
-    @Override
     public String toString() {
-        return this.functionIdentifier.getNamespace() + ":" + this.functionIdentifier.getName() + "@"
-                + this.functionIdentifier.getArity();
+        return functionIdentifier.toString();
     }
-
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IFunctionToDataSourceRewriter.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IFunctionToDataSourceRewriter.java
index 2793c4d..75a9689 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IFunctionToDataSourceRewriter.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IFunctionToDataSourceRewriter.java
@@ -24,8 +24,6 @@ import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
 
 public interface IFunctionToDataSourceRewriter {
-    public static final IFunctionToDataSourceRewriter NOOP = (o, c) -> false;
-
     /**
      * Replace the unnest operator by a datasource operator
      *
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java
index 91752f7..6cecaec 100644
--- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java
@@ -31,6 +31,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.asterix.om.functions.BuiltinFunctionInfo;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
 import org.apache.asterix.om.types.ATypeTag;
@@ -43,7 +44,6 @@ 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.functions.FunctionIdentifier;
-import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
 import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
@@ -110,50 +110,41 @@ public class ExceptionTest {
 
         // First, we will go over all the functions available, get their arity and type computers to know the number
         // of arguments to pass for each type computer
-        Map<IFunctionInfo, IResultTypeComputer> funTypeComputerMap;
+        Map<FunctionIdentifier, BuiltinFunctionInfo> registeredFunctions;
         try {
-            Field field = BuiltinFunctions.class.getDeclaredField("funTypeComputer");
+            Field field = BuiltinFunctions.class.getDeclaredField("registeredFunctions");
             field.setAccessible(true);
-            funTypeComputerMap = (HashMap<IFunctionInfo, IResultTypeComputer>) field.get(null);
+            registeredFunctions = (Map<FunctionIdentifier, BuiltinFunctionInfo>) field.get(null);
         } catch (Exception ex) {
             // this should never fail
             throw new IllegalStateException();
         }
 
-        if (funTypeComputerMap != null) {
-            // Note: If a type computer handles both known and VARARGS, VARARGS will be used
-            for (HashMap.Entry<IFunctionInfo, IResultTypeComputer> entry : funTypeComputerMap.entrySet()) {
-                // Some functions have a null type computer for some reason, ignore them
-                if (entry.getValue() == null) {
-                    continue;
-                }
+        // Note: If a type computer handles both known and VARARGS, VARARGS will be used
+        for (BuiltinFunctionInfo finfo : registeredFunctions.values()) {
+            FunctionIdentifier fid = finfo.getFunctionIdentifier();
+            IResultTypeComputer typeComputer = finfo.getResultTypeComputer();
 
-                // Type computer does not exist, add it with its arity to the map
-                if (typeComputerToArgsCountMap.get(entry.getValue().getClass().getSimpleName()) == null) {
-                    typeComputerToArgsCountMap.put(entry.getValue().getClass().getSimpleName(),
-                            entry.getKey().getFunctionIdentifier().getArity());
-                    continue;
-                }
+            // Type computer does not exist, add it with its arity to the map
+            if (typeComputerToArgsCountMap.get(typeComputer.getClass().getSimpleName()) == null) {
+                typeComputerToArgsCountMap.put(typeComputer.getClass().getSimpleName(), fid.getArity());
+                continue;
+            }
 
-                // VARARGS functions, these are kept as is, put/update, no need for any comparison
-                if (entry.getKey().getFunctionIdentifier().getArity() == FunctionIdentifier.VARARGS) {
-                    typeComputerToArgsCountMap.put(entry.getValue().getClass().getSimpleName(),
-                            entry.getKey().getFunctionIdentifier().getArity());
-                    continue;
-                }
+            // VARARGS functions, these are kept as is, put/update, no need for any comparison
+            if (fid.getArity() == FunctionIdentifier.VARARGS) {
+                typeComputerToArgsCountMap.put(typeComputer.getClass().getSimpleName(), fid.getArity());
+                continue;
+            }
 
-                // We have it already, and it is of type VARARGS, we are not going to change it
-                if (typeComputerToArgsCountMap
-                        .get(entry.getValue().getClass().getSimpleName()) == FunctionIdentifier.VARARGS) {
-                    continue;
-                }
+            // We have it already, and it is of type VARARGS, we are not going to change it
+            if (typeComputerToArgsCountMap.get(typeComputer.getClass().getSimpleName()) == FunctionIdentifier.VARARGS) {
+                continue;
+            }
 
-                // We have it already, if it has larger arity than our existing one, we will update it
-                if (typeComputerToArgsCountMap.get(entry.getValue().getClass().getSimpleName()) < entry.getKey()
-                        .getFunctionIdentifier().getArity()) {
-                    typeComputerToArgsCountMap.put(entry.getValue().getClass().getSimpleName(),
-                            entry.getKey().getFunctionIdentifier().getArity());
-                }
+            // We have it already, if it has larger arity than our existing one, we will update it
+            if (typeComputerToArgsCountMap.get(typeComputer.getClass().getSimpleName()) < fid.getArity()) {
+                typeComputerToArgsCountMap.put(typeComputer.getClass().getSimpleName(), fid.getArity());
             }
         }
 
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java
index 0db1821..1fc200c 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java
@@ -250,7 +250,7 @@ public class NonTaggedDataFormat implements IDataFormat {
                     IScalarEvaluatorFactory evalFactory = fDesc.createEvaluatorFactory(
                             new IScalarEvaluatorFactory[] { recordEvalFactory, fldIndexEvalFactory });
                     IFunctionInfo finfoAccess =
-                            BuiltinFunctions.getAsterixFunctionInfo(BuiltinFunctions.FIELD_ACCESS_BY_INDEX);
+                            BuiltinFunctions.getBuiltinFunctionInfo(BuiltinFunctions.FIELD_ACCESS_BY_INDEX);
 
                     ScalarFunctionCallExpression partitionFun = new ScalarFunctionCallExpression(finfoAccess,
                             new MutableObject<>(new VariableReferenceExpression(METADATA_DUMMY_VAR)),
@@ -275,7 +275,7 @@ public class NonTaggedDataFormat implements IDataFormat {
             fDesc.setImmutableStates(recType, fldName);
             IScalarEvaluatorFactory evalFactory =
                     fDesc.createEvaluatorFactory(new IScalarEvaluatorFactory[] { recordEvalFactory });
-            IFunctionInfo finfoAccess = BuiltinFunctions.getAsterixFunctionInfo(BuiltinFunctions.FIELD_ACCESS_NESTED);
+            IFunctionInfo finfoAccess = BuiltinFunctions.getBuiltinFunctionInfo(BuiltinFunctions.FIELD_ACCESS_NESTED);
 
             ScalarFunctionCallExpression partitionFun = new ScalarFunctionCallExpression(finfoAccess,
                     new MutableObject<>(new VariableReferenceExpression(METADATA_DUMMY_VAR)),
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/expressions/AbstractFunctionCallExpression.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/expressions/AbstractFunctionCallExpression.java
index 1cc206f..81aac03 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/expressions/AbstractFunctionCallExpression.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/expressions/AbstractFunctionCallExpression.java
@@ -21,10 +21,12 @@ package org.apache.hyracks.algebricks.core.algebra.expressions;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.commons.lang3.mutable.MutableObject;
@@ -54,23 +56,19 @@ public abstract class AbstractFunctionCallExpression extends AbstractLogicalExpr
 
     public AbstractFunctionCallExpression(FunctionKind kind, IFunctionInfo finfo,
             List<Mutable<ILogicalExpression>> arguments) {
-        this.kind = kind;
-        this.finfo = finfo;
-        this.arguments = arguments;
+        this.kind = Objects.requireNonNull(kind);
+        this.finfo = Objects.requireNonNull(finfo);
+        this.arguments = Objects.requireNonNull(arguments);
     }
 
     public AbstractFunctionCallExpression(FunctionKind kind, IFunctionInfo finfo) {
-        this.kind = kind;
-        this.finfo = finfo;
-        this.arguments = new ArrayList<Mutable<ILogicalExpression>>();
+        this(kind, finfo, new ArrayList<>());
     }
 
     public AbstractFunctionCallExpression(FunctionKind kind, IFunctionInfo finfo,
             Mutable<ILogicalExpression>... expressions) {
         this(kind, finfo);
-        for (Mutable<ILogicalExpression> e : expressions) {
-            this.arguments.add(e);
-        }
+        Collections.addAll(arguments, expressions);
     }
 
     public void setOpaqueParameters(Object[] opaqueParameters) {
@@ -212,7 +210,7 @@ public abstract class AbstractFunctionCallExpression extends AbstractLogicalExpr
 
     @Override
     public int hashCode() {
-        int h = finfo.hashCode();
+        int h = getFunctionIdentifier().hashCode();
         for (Mutable<ILogicalExpression> e : arguments) {
             h = h * 41 + e.getValue().hashCode();
         }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/functions/FunctionIdentifier.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/functions/FunctionIdentifier.java
index 74c1ba5..db5a959 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/functions/FunctionIdentifier.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/functions/FunctionIdentifier.java
@@ -19,6 +19,7 @@
 package org.apache.hyracks.algebricks.core.algebra.functions;
 
 import java.io.Serializable;
+import java.util.Objects;
 
 public class FunctionIdentifier implements Serializable {
     private static final long serialVersionUID = 1L;
@@ -29,20 +30,24 @@ public class FunctionIdentifier implements Serializable {
 
     public static final int VARARGS = -1;
 
-    public FunctionIdentifier(String namespace, String name) {
-        this(namespace, name, VARARGS);
-    }
-
     public FunctionIdentifier(String namespace, String name, int arity) {
         this.namespace = namespace;
         this.name = name;
         this.arity = arity;
     }
 
+    public String getNamespace() {
+        return namespace;
+    }
+
     public String getName() {
         return name;
     }
 
+    public int getArity() {
+        return arity;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (super.equals(o)) {
@@ -50,26 +55,18 @@ public class FunctionIdentifier implements Serializable {
         }
         if (o instanceof FunctionIdentifier) {
             FunctionIdentifier ofi = (FunctionIdentifier) o;
-            return ofi.getNamespace().equals(getNamespace()) && ofi.name.equals(name);
+            return namespace.equals(ofi.namespace) && name.equals(ofi.name) && arity == ofi.arity;
         }
         return false;
     }
 
     @Override
     public int hashCode() {
-        return name.hashCode() + namespace.hashCode();
+        return Objects.hash(namespace, name, arity);
     }
 
     @Override
     public String toString() {
-        return getNamespace() + ":" + name;
-    }
-
-    public int getArity() {
-        return arity;
-    }
-
-    public String getNamespace() {
-        return namespace;
+        return namespace + ':' + name + '#' + arity;
     }
 }