[NO ISSUE][COMP] UDF improvements

- user model changes: yes
- storage format changes: no
- interface changes: no

Details:
- SQL++ grammar changes
   1. CREATE FUNCTON
      Support AS .. AT .. for external identifier
      Remove LANGUAGE, INLINE
      Move WITH record contents into its 'resources' field
      Move DETERMINISTIC, NULLCALL into WITH fields
   2. CREATE ADAPTER
      Support AS .. AT .. for external identifier
   3. Add DROP ADAPTER
- CreateFunctionStatement, CreateAdapterStatement
   1. Add library dataverse name
   2. Support IF NOT EXISTS
   3. Remove language
- Support IF EXISTS in AdapterDropStatement and
  LibraryDropStatement
- Handle AdapterDropStatement in QueryTranslator
- FunctionTupleTranslator writes external identifier as
  an ordered list instead of encoding it into function body
- Changed external identifier format for Python UDFs to
  "package.module", "class.function"
- Add validation for external identifier depending
  on the library language
- Update documentation and testcases

Change-Id: I3f2125b5508f2321cfa9b561b5250d6255135f91
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/7483
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
Reviewed-by: Ian Maxon <imaxon@uci.edu>
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 a00933a..8f5bd9c 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
@@ -20,8 +20,8 @@
 package org.apache.asterix.optimizer.rules;
 
 import org.apache.asterix.lang.common.util.FunctionUtil;
-import org.apache.asterix.metadata.functions.ExternalScalarFunctionInfo;
 import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.ExternalFunctionInfo;
 import org.apache.asterix.om.typecomputer.base.TypeCastUtils;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.ATypeTag;
@@ -78,7 +78,7 @@
         for (int i = 0; i < funcCallExpr.getArguments().size(); i++) {
             Mutable<ILogicalExpression> argExpr = funcCallExpr.getArguments().get(i);
             inputType = (IAType) op.computeOutputTypeEnvironment(context).getType(argExpr.getValue());
-            reqArgType = ((ExternalScalarFunctionInfo) funcCallExpr.getFunctionInfo()).getArgumentTypes().get(i);
+            reqArgType = ((ExternalFunctionInfo) funcCallExpr.getFunctionInfo()).getParameterTypes().get(i);
 
             if (reqArgType.getTypeTag() == ATypeTag.OBJECT) {
                 castFlag = !IntroduceDynamicTypeCastRule.compatible((ARecordType) reqArgType, inputType,
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UdfApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UdfApiServlet.java
index d0ae6ac..d5bec4f 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UdfApiServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UdfApiServlet.java
@@ -238,7 +238,7 @@
         }
         try {
             IRequestReference requestReference = receptionist.welcome(request);
-            LibraryDropStatement stmt = new LibraryDropStatement(libraryName.first, libraryName.second);
+            LibraryDropStatement stmt = new LibraryDropStatement(libraryName.first, libraryName.second, false);
             executeStatement(stmt, requestReference);
             response.setStatus(HttpResponseStatus.OK);
         } catch (Exception e) {
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 3e64d37..04a2f2d 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
@@ -32,7 +32,6 @@
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
@@ -101,6 +100,7 @@
 import org.apache.asterix.lang.common.expression.IndexedTypeExpression;
 import org.apache.asterix.lang.common.expression.TypeExpression;
 import org.apache.asterix.lang.common.expression.TypeReferenceExpression;
+import org.apache.asterix.lang.common.statement.AdapterDropStatement;
 import org.apache.asterix.lang.common.statement.CompactStatement;
 import org.apache.asterix.lang.common.statement.ConnectFeedStatement;
 import org.apache.asterix.lang.common.statement.CreateAdapterStatement;
@@ -355,12 +355,15 @@
                     case NODEGROUP_DROP:
                         handleNodegroupDropStatement(metadataProvider, stmt);
                         break;
-                    case CREATE_FUNCTION:
-                        handleCreateFunctionStatement(metadataProvider, stmt, stmtRewriter);
-                        break;
                     case CREATE_ADAPTER:
                         handleCreateAdapterStatement(metadataProvider, stmt);
                         break;
+                    case ADAPTER_DROP:
+                        handleAdapterDropStatement(metadataProvider, stmt);
+                        break;
+                    case CREATE_FUNCTION:
+                        handleCreateFunctionStatement(metadataProvider, stmt, stmtRewriter);
+                        break;
                     case FUNCTION_DROP:
                         handleFunctionDropStatement(metadataProvider, stmt);
                         break;
@@ -1987,11 +1990,19 @@
             IStatementRewriter stmtRewriter) throws Exception {
         CreateFunctionStatement cfs = (CreateFunctionStatement) stmt;
         FunctionSignature signature = cfs.getFunctionSignature();
-        signature.setDataverseName(getActiveDataverseName(signature.getDataverseName()));
-        String libraryName = cfs.getLibName();
+        DataverseName dataverseName = getActiveDataverseName(signature.getDataverseName());
+        signature.setDataverseName(dataverseName);
+        DataverseName libraryDataverseName = null;
+        String libraryName = cfs.getLibraryName();
+        if (libraryName != null) {
+            libraryDataverseName = cfs.getLibraryDataverseName();
+            if (libraryDataverseName == null) {
+                libraryDataverseName = dataverseName;
+            }
+        }
 
-        lockUtil.createFunctionBegin(lockManager, metadataProvider.getLocks(), signature.getDataverseName(),
-                signature.getName(), libraryName);
+        lockUtil.createFunctionBegin(lockManager, metadataProvider.getLocks(), dataverseName, signature.getName(),
+                libraryDataverseName, libraryName);
         try {
             doCreateFunction(metadataProvider, cfs, signature, stmtRewriter);
         } finally {
@@ -2003,7 +2014,6 @@
     protected void doCreateFunction(MetadataProvider metadataProvider, CreateFunctionStatement cfs,
             FunctionSignature signature, IStatementRewriter stmtRewriter) throws Exception {
         DataverseName dataverseName = signature.getDataverseName();
-        String libraryName = cfs.getLibName();
         SourceLocation sourceLoc = cfs.getSourceLocation();
         MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
         metadataProvider.setMetadataTxnContext(mdTxnCtx);
@@ -2012,8 +2022,17 @@
             if (dv == null) {
                 throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverseName);
             }
-            boolean isExternal = cfs.isExternal();
+            Function function = MetadataManager.INSTANCE.getFunction(mdTxnCtx, signature);
+            if (function != null) {
+                if (cfs.getIfNotExists()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return;
+                }
+                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);
@@ -2035,42 +2054,35 @@
                 }
             }
 
-            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 (isExternal) {
-                String lang = cfs.getLang();
-                if (lang == null) {
-                    throw new CompilationException(ErrorCode.COMPILATION_INCOMPATIBLE_FUNCTION_LANGUAGE, sourceLoc, "");
+                TypeExpression returnTypeExpr = cfs.getReturnType();
+                Pair<TypeSignature, TypeSignature> returnType = translateFunctionParameterType(signature, -1,
+                        returnTypeExpr, isExternal, sourceLoc, metadataProvider, mdTxnCtx);
+                if (returnType.second != null) {
+                    dependentTypes.add(returnType.second);
                 }
-                ExternalFunctionLanguage functionLang;
-                try {
-                    functionLang = ExternalFunctionLanguage.valueOf(lang.toUpperCase(Locale.ROOT));
-                } catch (IllegalArgumentException e) {
-                    throw new CompilationException(ErrorCode.COMPILATION_INCOMPATIBLE_FUNCTION_LANGUAGE, sourceLoc,
-                            lang);
+                DataverseName libraryDataverseName = cfs.getLibraryDataverseName();
+                if (libraryDataverseName == null) {
+                    libraryDataverseName = dataverseName;
                 }
-                Library libraryInMetadata = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, dataverseName, libraryName);
-                if (libraryInMetadata == null) {
+                String libraryName = cfs.getLibraryName();
+                Library library = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, libraryDataverseName, libraryName);
+                if (library == null) {
                     throw new CompilationException(ErrorCode.UNKNOWN_LIBRARY, sourceLoc, libraryName);
                 }
-                // Add functions
-                String body = ExternalFunctionCompilerUtil.encodeExternalIdentifier(signature, functionLang,
-                        cfs.getExternalIdentifier());
+
+                ExternalFunctionLanguage language =
+                        ExternalFunctionCompilerUtil.getExternalFunctionLanguage(library.getLanguage());
+                List<String> externalIdentifier = cfs.getExternalIdentifier();
+                ExternalFunctionCompilerUtil.validateExternalIdentifier(externalIdentifier, language,
+                        cfs.getSourceLocation());
+
                 List<List<Triple<DataverseName, String, String>>> dependencies =
                         FunctionUtil.getExternalFunctionDependencies(dependentTypes);
-                Function f = new Function(signature, paramNames, paramTypes, returnType.first, body,
-                        FunctionKind.SCALAR.toString(), functionLang.name(), libraryName, cfs.getNullCall(),
-                        cfs.getDeterministic(), cfs.getResources(), dependencies);
-                MetadataManager.INSTANCE.addFunction(mdTxnCtx, f);
-                if (LOGGER.isInfoEnabled()) {
-                    LOGGER.info("Installed function: " + signature);
-                }
-                MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                function = new Function(signature, paramNames, paramTypes, returnType.first, null,
+                        FunctionKind.SCALAR.toString(), library.getLanguage(), libraryDataverseName, libraryName,
+                        externalIdentifier, cfs.getNullCall(), cfs.getDeterministic(), cfs.getResources(),
+                        dependencies);
             } else {
                 //Check whether the function is use-able
                 metadataProvider.setDefaultDataverse(dv);
@@ -2083,15 +2095,17 @@
                 List<List<Triple<DataverseName, String, String>>> dependencies =
                         FunctionUtil.getFunctionDependencies(rewriterFactory.createQueryRewriter(),
                                 cfs.getFunctionBodyExpression(), metadataProvider, dependentTypes);
-                Function function = new Function(signature, paramNames, paramTypes, returnType.first,
+                function = new Function(signature, paramNames, paramTypes, TypeUtil.ANY_TYPE_SIGNATURE,
                         cfs.getFunctionBody(), FunctionKind.SCALAR.toString(),
-                        compilationProvider.getParserFactory().getLanguage(), null, null, null, null, dependencies);
-                MetadataManager.INSTANCE.addFunction(mdTxnCtx, function);
-                if (LOGGER.isInfoEnabled()) {
-                    LOGGER.info("Installed function: " + signature);
-                }
-                MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                        compilationProvider.getParserFactory().getLanguage(), null, null, null, null, null, null,
+                        dependencies);
             }
+
+            MetadataManager.INSTANCE.addFunction(mdTxnCtx, function);
+            if (LOGGER.isInfoEnabled()) {
+                LOGGER.info("Installed function: " + signature);
+            }
+            MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
         } catch (Exception e) {
             abort(e, e, mdTxnCtx);
             throw e;
@@ -2153,45 +2167,6 @@
         return new Pair<>(resultType, dependentType);
     }
 
-    protected void handleCreateAdapterStatement(MetadataProvider metadataProvider, Statement stmt) throws Exception {
-        CreateAdapterStatement cas = (CreateAdapterStatement) stmt;
-        SourceLocation sourceLoc = cas.getSourceLocation();
-        AdapterIdentifier aid = cas.getAdapterId();
-        DataverseName dataverse = getActiveDataverseName(aid.getDataverseName());
-        if (!dataverse.equals(aid.getDataverseName())) {
-            aid = new AdapterIdentifier(dataverse, aid.getName());
-        }
-        MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
-        metadataProvider.setMetadataTxnContext(mdTxnCtx);
-        String libraryName = cas.getLibName();
-        lockUtil.createAdapterBegin(lockManager, metadataProvider.getLocks(), dataverse, aid.getName(), libraryName);
-        try {
-            Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverse);
-            if (dv == null) {
-                throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverse);
-            }
-            Library libraryInMetadata = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, dataverse, libraryName);
-            if (libraryInMetadata == null) {
-                throw new CompilationException(ErrorCode.UNKNOWN_LIBRARY, sourceLoc, libraryName);
-            }
-            // Add adapters
-            String adapterFactoryClass = cas.getExternalIdent();
-            DatasourceAdapter dsa = new DatasourceAdapter(aid, adapterFactoryClass,
-                    IDataSourceAdapter.AdapterType.EXTERNAL, libraryName);
-            MetadataManager.INSTANCE.addAdapter(mdTxnCtx, dsa);
-            if (LOGGER.isInfoEnabled()) {
-                LOGGER.info("Installed adapter: " + aid.getName());
-            }
-            MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
-
-        } catch (Exception e) {
-            abort(e, e, mdTxnCtx);
-            throw e;
-        } finally {
-            metadataProvider.getLocks().unlock();
-        }
-    }
-
     protected void handleFunctionDropStatement(MetadataProvider metadataProvider, Statement stmt) throws Exception {
         FunctionDropStatement stmtDropFunction = (FunctionDropStatement) stmt;
         FunctionSignature signature = stmtDropFunction.getFunctionSignature();
@@ -2211,6 +2186,16 @@
         MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
         metadataProvider.setMetadataTxnContext(mdTxnCtx);
         try {
+            Dataverse dataverse = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, signature.getDataverseName());
+            if (dataverse == null) {
+                if (stmtDropFunction.getIfExists()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return false;
+                } else {
+                    throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc,
+                            signature.getDataverseName());
+                }
+            }
             Function function = MetadataManager.INSTANCE.getFunction(mdTxnCtx, signature);
             if (function == null) {
                 if (stmtDropFunction.getIfExists()) {
@@ -2230,6 +2215,124 @@
         }
     }
 
+    protected void handleCreateAdapterStatement(MetadataProvider metadataProvider, Statement stmt) throws Exception {
+        CreateAdapterStatement cas = (CreateAdapterStatement) stmt;
+        DataverseName dataverseName = getActiveDataverseName(cas.getDataverseName());
+        DataverseName libraryDataverseName = cas.getLibraryDataverseName();
+        if (libraryDataverseName == null) {
+            libraryDataverseName = dataverseName;
+        }
+        String libraryName = cas.getLibraryName();
+        lockUtil.createAdapterBegin(lockManager, metadataProvider.getLocks(), dataverseName, cas.getAdapterName(),
+                libraryDataverseName, libraryName);
+        try {
+            doCreateAdapter(metadataProvider, cas);
+        } finally {
+            metadataProvider.getLocks().unlock();
+        }
+    }
+
+    protected void doCreateAdapter(MetadataProvider metadataProvider, CreateAdapterStatement cas) throws Exception {
+        SourceLocation sourceLoc = cas.getSourceLocation();
+        MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
+        metadataProvider.setMetadataTxnContext(mdTxnCtx);
+        try {
+            DataverseName dataverseName = getActiveDataverseName(cas.getDataverseName());
+            Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverseName);
+            if (dv == null) {
+                throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverseName);
+            }
+            String adapterName = cas.getAdapterName();
+            DatasourceAdapter adapter = MetadataManager.INSTANCE.getAdapter(mdTxnCtx, dataverseName, adapterName);
+            if (adapter != null) {
+                if (cas.getIfNotExists()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return;
+                }
+                throw new CompilationException(ErrorCode.ADAPTER_EXISTS, sourceLoc, adapterName);
+            }
+
+            DataverseName libraryDataverseName = cas.getLibraryDataverseName();
+            if (libraryDataverseName == null) {
+                libraryDataverseName = dataverseName;
+            }
+            String libraryName = cas.getLibraryName();
+            Library library = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, libraryDataverseName, libraryName);
+            if (library == null) {
+                throw new CompilationException(ErrorCode.UNKNOWN_LIBRARY, sourceLoc, libraryName);
+            }
+            // Add adapters
+            ExternalFunctionLanguage language =
+                    ExternalFunctionCompilerUtil.getExternalFunctionLanguage(library.getLanguage());
+            List<String> externalIdentifier = cas.getExternalIdentifier();
+            ExternalFunctionCompilerUtil.validateExternalIdentifier(externalIdentifier, language,
+                    cas.getSourceLocation());
+
+            if (language != ExternalFunctionLanguage.JAVA) {
+                throw new CompilationException(ErrorCode.UNSUPPORTED_ADAPTER_LANGUAGE, cas.getSourceLocation(),
+                        language.name());
+            }
+            String adapterFactoryClass = externalIdentifier.get(0);
+
+            adapter = new DatasourceAdapter(new AdapterIdentifier(dataverseName, adapterName),
+                    IDataSourceAdapter.AdapterType.EXTERNAL, adapterFactoryClass, libraryDataverseName, libraryName);
+            MetadataManager.INSTANCE.addAdapter(mdTxnCtx, adapter);
+            if (LOGGER.isInfoEnabled()) {
+                LOGGER.info("Installed adapter: " + adapterName);
+            }
+            MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+        } catch (Exception e) {
+            abort(e, e, mdTxnCtx);
+            throw e;
+        }
+    }
+
+    protected void handleAdapterDropStatement(MetadataProvider metadataProvider, Statement stmt) throws Exception {
+        AdapterDropStatement stmtDropAdapter = (AdapterDropStatement) stmt;
+        DataverseName dataverseName = getActiveDataverseName(stmtDropAdapter.getDataverseName());
+        String adapterName = stmtDropAdapter.getAdapterName();
+        lockUtil.dropAdapterBegin(lockManager, metadataProvider.getLocks(), dataverseName, adapterName);
+        try {
+            doDropAdapter(metadataProvider, stmtDropAdapter, dataverseName, adapterName);
+        } finally {
+            metadataProvider.getLocks().unlock();
+        }
+    }
+
+    protected boolean doDropAdapter(MetadataProvider metadataProvider, AdapterDropStatement stmtDropAdapter,
+            DataverseName dataverseName, String adapterName) throws Exception {
+        SourceLocation sourceLoc = stmtDropAdapter.getSourceLocation();
+        MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
+        metadataProvider.setMetadataTxnContext(mdTxnCtx);
+        try {
+            Dataverse dataverse = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverseName);
+            if (dataverse == null) {
+                if (stmtDropAdapter.getIfExists()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return false;
+                } else {
+                    throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverseName);
+                }
+            }
+            DatasourceAdapter adapter = MetadataManager.INSTANCE.getAdapter(mdTxnCtx, dataverseName, adapterName);
+            if (adapter == null) {
+                if (stmtDropAdapter.getIfExists()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return false;
+                } else {
+                    throw new CompilationException(ErrorCode.UNKNOWN_ADAPTER, sourceLoc, adapterName);
+                }
+            }
+
+            MetadataManager.INSTANCE.dropAdapter(mdTxnCtx, dataverseName, adapterName);
+            MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+            return true;
+        } catch (Exception e) {
+            abort(e, e, mdTxnCtx);
+            throw e;
+        }
+    }
+
     protected void handleCreateLibraryStatement(MetadataProvider metadataProvider, Statement stmt,
             IHyracksClientConnection hcc) throws Exception {
         CreateLibraryStatement cls = (CreateLibraryStatement) stmt;
@@ -2360,26 +2463,38 @@
         String libraryName = stmtDropLibrary.getLibraryName();
         lockUtil.dropLibraryBegin(lockManager, metadataProvider.getLocks(), dataverseName, libraryName);
         try {
-            doDropLibrary(metadataProvider, dataverseName, libraryName, hcc);
+            doDropLibrary(metadataProvider, stmtDropLibrary, dataverseName, libraryName, hcc);
         } finally {
             metadataProvider.getLocks().unlock();
         }
     }
 
-    private void doDropLibrary(MetadataProvider metadataProvider, DataverseName dataverseName, String libraryName,
-            IHyracksClientConnection hcc) throws Exception {
+    protected boolean doDropLibrary(MetadataProvider metadataProvider, LibraryDropStatement stmtDropLibrary,
+            DataverseName dataverseName, String libraryName, IHyracksClientConnection hcc) throws Exception {
         JobUtils.ProgressState progress = ProgressState.NO_PROGRESS;
         MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
         boolean bActiveTxn = true;
         metadataProvider.setMetadataTxnContext(mdTxnCtx);
         try {
-            Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverseName);
-            if (dv == null) {
-                throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, dataverseName);
+            Dataverse dataverse = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverseName);
+            if (dataverse == null) {
+                if (stmtDropLibrary.getIfExists()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return false;
+                } else {
+                    throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, stmtDropLibrary.getSourceLocation(),
+                            dataverseName);
+                }
             }
             Library library = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, dataverseName, libraryName);
             if (library == null) {
-                throw new CompilationException(ErrorCode.UNKNOWN_LIBRARY, libraryName);
+                if (stmtDropLibrary.getIfExists()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return false;
+                } else {
+                    throw new CompilationException(ErrorCode.UNKNOWN_LIBRARY, stmtDropLibrary.getSourceLocation(),
+                            libraryName);
+                }
             }
 
             // #. mark the existing library as PendingDropOp
@@ -2408,6 +2523,7 @@
             MetadataManager.INSTANCE.dropLibrary(mdTxnCtx, dataverseName, libraryName);
 
             MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+            return true;
         } catch (Exception e) {
             if (bActiveTxn) {
                 abort(e, e, mdTxnCtx);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/deterministic/deterministic.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/deterministic/deterministic.2.ddl.sqlpp
index 8439cfb..0d700f2 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/deterministic/deterministic.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/deterministic/deterministic.2.ddl.sqlpp
@@ -20,10 +20,12 @@
 use externallibtest;
 
 create function getCapital_default(a: string) returns CountryCapitalType
-language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function getCapital_deterministic(a: string) returns CountryCapitalType
-language java deterministic as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib
+  with { "deterministic": true };
 
 create function getCapital_not_deterministic(a: string) returns CountryCapitalType
-language java not deterministic as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
\ No newline at end of file
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib
+  with { "deterministic": false };
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
index a03b570..59c0509 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
@@ -19,4 +19,5 @@
 
 use externallibtest;
 
-create function getCapital(a: string) returns CountryCapitalType language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+create function getCapital(a: string) returns CountryCapitalType
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital_open/getCapital_open.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital_open/getCapital_open.2.ddl.sqlpp
index 28963b6..5532ae7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital_open/getCapital_open.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital_open/getCapital_open.2.ddl.sqlpp
@@ -19,4 +19,5 @@
 
 use externallibtest;
 
-create function getCapital(a) language java as "testlib","org.apache.asterix.external.library.OpenCapitalFinderFactory";
+create function getCapital(a)
+  as "org.apache.asterix.external.library.OpenCapitalFinderFactory" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/keyword_detector/keyword_detector.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/keyword_detector/keyword_detector.2.ddl.sqlpp
index 2ff4d11..80502a0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/keyword_detector/keyword_detector.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/keyword_detector/keyword_detector.2.ddl.sqlpp
@@ -24,6 +24,11 @@
 
 use test;
 
-create function fnameDetector(a: InputRecordType) returns DetectResultType language java as "testlib","org.apache.asterix.external.library.KeywordsDetectorFactory" WITH {"dictPath":"data/external_function/KeywordsDetector_List1.txt","fieldName":"fname"};
-create function lnameDetector(a: InputRecordType) returns DetectResultType language java as "testlib","org.apache.asterix.external.library.KeywordsDetectorFactory" WITH {"dictPath":"data/external_function/KeywordsDetector_List2.txt","fieldName":"lname"};
+create function fnameDetector(a: InputRecordType) returns DetectResultType
+  as "org.apache.asterix.external.library.KeywordsDetectorFactory" at testlib
+  with { "resources": {"dictPath":"data/external_function/KeywordsDetector_List1.txt","fieldName":"fname"} };
+
+create function lnameDetector(a: InputRecordType) returns DetectResultType
+  as "org.apache.asterix.external.library.KeywordsDetectorFactory" at testlib
+  with { "resources": {"dictPath":"data/external_function/KeywordsDetector_List2.txt","fieldName":"lname"} };
 
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.0.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.0.ddl.sqlpp
index 76cc70d..05b3d2c 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.0.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.0.ddl.sqlpp
@@ -18,3 +18,6 @@
  */
 DROP DATAVERSE externallibtest if exists;
 CREATE DATAVERSE  externallibtest;
+
+DROP DATAVERSE externallibtest2 if exists;
+CREATE DATAVERSE  externallibtest2;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.1.lib.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.1.lib.sqlpp
index 3dc6eb6..9e53153 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.1.lib.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.1.lib.sqlpp
@@ -16,4 +16,4 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-install externallibtest testlib admin admin target/data/externallib/asterix-external-data-testlib.zip
+install externallibtest2 testlib admin admin target/data/externallib/asterix-external-data-testlib.zip
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.2.ddl.sqlpp
index 25f04e1..aeeba56 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.2.ddl.sqlpp
@@ -16,7 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
- use externallibtest;
 
+/*
+ * Use library in a different dataverse
+ */
 
-create function my_array_sum(a: [int32]) returns int32 language java as "testlib","org.apache.asterix.external.library.MyArraySumFactory";
+use externallibtest;
+
+create function my_array_sum(a: [int32]) returns int32
+  as "org.apache.asterix.external.library.MyArraySumFactory" at externallibtest2.testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.4.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.4.ddl.sqlpp
index 65733e4..45afe0c 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.4.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/my_array_sum/my_array_sum.4.ddl.sqlpp
@@ -17,3 +17,4 @@
  * under the License.
  */
 DROP DATAVERSE externallibtest;
+DROP DATAVERSE externallibtest2;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysentiment/mysentiment.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysentiment/mysentiment.2.ddl.sqlpp
index ff39aee..00838de 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysentiment/mysentiment.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysentiment/mysentiment.2.ddl.sqlpp
@@ -18,4 +18,5 @@
  */
  USE externallibtest;
 
-create function sentiment(s) language python as "testlib","sentiment:TweetSent";
+create function sentiment(s)
+  as "sentiment", "TweetSent.sentiment" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum/mysum.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum/mysum.2.ddl.sqlpp
index fd815d1..9f325a3 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum/mysum.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum/mysum.2.ddl.sqlpp
@@ -16,6 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
- USE externallibtest;
 
-create function mysum(a: int32, b: int32) returns int32 language java as "testlib","org.apache.asterix.external.library.MySumFactory";
+create function externallibtest.mysum(a: int32, b: int32) returns int32
+  as "org.apache.asterix.external.library.MySumFactory" at externallibtest.testlib;
+
+/* test if not exists */
+create function externallibtest.mysum if not exists (a: int32, b: int32) returns int32
+  as "org.apache.asterix.external.library.MySumFactory" at externallibtest.testlib;
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.2.ddl.sqlpp
index fd815d1..4e93750 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysum_dropinuse/mysum_dropinuse.2.ddl.sqlpp
@@ -18,4 +18,5 @@
  */
  USE externallibtest;
 
-create function mysum(a: int32, b: int32) returns int32 language java as "testlib","org.apache.asterix.external.library.MySumFactory";
+create function mysum(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/py_function_error/py_function_error.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/py_function_error/py_function_error.2.ddl.sqlpp
index 5b8f8bd..6bbeaa1 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/py_function_error/py_function_error.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/py_function_error/py_function_error.2.ddl.sqlpp
@@ -19,4 +19,5 @@
 
 use test;
 
-create function warning() language python as "testlib","roundtrip:Tests";
+create function warning()
+  as "roundtrip", "Tests.warning" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/py_nested_access/py_nested_access.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/py_nested_access/py_nested_access.2.ddl.sqlpp
index 655036c..69395a0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/py_nested_access/py_nested_access.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/py_nested_access/py_nested_access.2.ddl.sqlpp
@@ -19,4 +19,5 @@
 
 use test;
 
-create function roundtrip(s) language python as "testlib","roundtrip:Tests";
+create function roundtrip(s)
+  as "roundtrip", "Tests.roundtrip" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/python-fn-escape/python-fn-escape.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/python-fn-escape/python-fn-escape.2.ddl.sqlpp
index b1c5b10..ebb0844 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/python-fn-escape/python-fn-escape.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/python-fn-escape/python-fn-escape.2.ddl.sqlpp
@@ -18,4 +18,5 @@
  */
  USE externallibtest;
 
-create function system(s: string) language python as "testlib","os";
+create function system(s: string)
+  as "os", "system" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/return_invalid_type/return_invalid_type.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/return_invalid_type/return_invalid_type.2.ddl.sqlpp
index 82fec18..ee3c27a 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/return_invalid_type/return_invalid_type.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/return_invalid_type/return_invalid_type.2.ddl.sqlpp
@@ -19,4 +19,5 @@
 
 use externallibtest;
 
-create function addHashTagsInPlace(t: Tweet) returns ProcessedTweet language java as "testlib","org.apache.asterix.external.library.AddHashTagsInPlaceFactory";
+create function addHashTagsInPlace(t: Tweet) returns ProcessedTweet
+  as "org.apache.asterix.external.library.AddHashTagsInPlaceFactory" at testlib;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/type_validation/type_validation.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/type_validation/type_validation.2.ddl.sqlpp
index f87c3d7..2a7c683 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/type_validation/type_validation.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/type_validation/type_validation.2.ddl.sqlpp
@@ -18,5 +18,6 @@
  */
 use externallibtest;
 
-create function typeValidation(a: int32, b: float, c:string, d:double, e:boolean, f:date, g: datetime) returns string language java as "testlib","org.apache.asterix.external.library.TypeValidationFunctionFactory";
+create function typeValidation(a: int32, b: float, c:string, d:double, e:boolean, f:date, g: datetime) returns string
+  as "org.apache.asterix.external.library.TypeValidationFunctionFactory" at testlib;
 
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/udf_metadata/udf_metadata.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/udf_metadata/udf_metadata.2.ddl.sqlpp
index bcb86ac..c9623b5 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/udf_metadata/udf_metadata.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/udf_metadata/udf_metadata.2.ddl.sqlpp
@@ -20,35 +20,35 @@
 use externallibtest;
 
 create function myfn001()
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function myfn002(a)
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function myfn003(a:string, b:[bigint], c:{{boolean}})
   returns string
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function myfn004(a:CountryCapitalType, b:[CountryCapitalType])
   returns CountryCapitalType
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function myfn005(a:smallint, b:[bigint], c:CountryCapitalType, d:[CountryCapitalType])
   returns string
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function myfn006(a [string])
   returns [string]
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function myfn007(a {{string}})
   returns {{string}}
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function myfn008(a [CountryCapitalType])
   returns [CountryCapitalType]
-  language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib;
 
 create function externallibtest2.myfn009(a externallibtest.CountryCapitalType)
   returns externallibtest.CountryCapitalType
-  language java as "testlib2","org.apache.asterix.external.library.CapitalFinderFactory";
\ No newline at end of file
+  as "org.apache.asterix.external.library.CapitalFinderFactory" at testlib2;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/upperCase/upperCase.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/upperCase/upperCase.2.ddl.sqlpp
index 7b12324..a6cf8fb 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/upperCase/upperCase.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/upperCase/upperCase.2.ddl.sqlpp
@@ -18,5 +18,6 @@
  */
 USE externallibtest;
 
-create function toUpper(t: TextType) returns TextType language java as "testlib","org.apache.asterix.external.library.UpperCaseFactory";
+create function toUpper(t: TextType) returns TextType
+  as "org.apache.asterix.external.library.UpperCaseFactory" at testlib;
 
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.0.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.0.ddl.sqlpp
new file mode 100644
index 0000000..904e960
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.0.ddl.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Feed with an external adapter. Test different dataverses.
+ * Expected Res : Success
+ */
+
+drop dataverse externallibtest if exists;
+create dataverse externallibtest;
+
+drop dataverse externallibtest2 if exists;
+create dataverse externallibtest2;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.1.lib.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.1.lib.sqlpp
new file mode 100644
index 0000000..806ad5e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.1.lib.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
+
+install externallibtest2 testlib admin admin target/data/externallib/asterix-external-data-testlib.zip
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.2.ddl.sqlpp
new file mode 100644
index 0000000..46a5fe9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.2.ddl.sqlpp
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
+
+/* test drop adapter if exists */
+drop adapter externallibtest.TweetAdapter if exists;
+
+create adapter externallibtest.TweetAdapter
+  as "org.apache.asterix.external.library.adapter.TestTypedAdapterFactory" at externallibtest2.testlib;
+
+/* test create adapter if not exists */
+create adapter externallibtest.TweetAdapter if not exists
+  as "org.apache.asterix.external.library.adapter.TestTypedAdapterFactory" at externallibtest2.testlib;
+
+create type externallibtest.TweetType as open {
+    tweetid: int64
+};
+
+create dataset externallibtest.Tweets(TweetType) primary key tweetid;
+
+create feed externallibtest.TweetFeed with {
+  "adapter-name": "TweetAdapter",
+  "type-name" : "TweetType",
+  "num_output_records": 4
+};
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.3.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.3.update.sqlpp
new file mode 100644
index 0000000..ae44476
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.3.update.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
+
+set `wait-for-completion-feed` "true";
+
+connect feed externallibtest.TweetFeed to dataset externallibtest.Tweets;
+start feed externallibtest.TweetFeed;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.4.query.sqlpp
new file mode 100644
index 0000000..5eccf60
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.4.query.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
+
+select value t
+from externallibtest.Tweets t
+order by t.tweetid;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.5.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.5.update.sqlpp
new file mode 100644
index 0000000..62f4960
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.5.update.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
+
+disconnect feed externallibtest.TweetFeed from dataset externallibtest.Tweets;
+drop feed externallibtest.TweetFeed;
+drop adapter externallibtest.TweetAdapter;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.2.ddl.sqlpp
index 9cb4046..f0a36cd 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.2.ddl.sqlpp
@@ -23,8 +23,15 @@
 
 use externallibtest;
 
-create adapter TweetAdapter language java as "testlib",
-  "org.apache.asterix.external.library.adapter.TestTypedAdapterFactory";
+/* test drop adapter if exists */
+drop adapter TweetAdapter if exists;
+
+create adapter TweetAdapter
+  as "org.apache.asterix.external.library.adapter.TestTypedAdapterFactory" at testlib;
+
+/* test create adapter if not exists */
+create adapter TweetAdapter if not exists
+  as "org.apache.asterix.external.library.adapter.TestTypedAdapterFactory" at testlib;
 
 create type TweetType as open {
     tweetid: int64
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.5.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.5.update.sqlpp
new file mode 100644
index 0000000..e897463
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.5.update.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
+
+use externallibtest;
+
+disconnect feed TweetFeed from dataset Tweets;
+drop feed TweetFeed;
+drop adapter TweetAdapter;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-function/feed-with-external-function.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-function/feed-with-external-function.2.ddl.sqlpp
index 00544be..01e29c0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-function/feed-with-external-function.2.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-function/feed-with-external-function.2.ddl.sqlpp
@@ -24,5 +24,7 @@
 
 use udfs;
 
-create function addMentionedUsers(t: TweetType) returns TweetType language java as "testlib","org.apache.asterix.external.library.AddMentionedUsersFactory" WITH {"textFieldName": "text"};
+create function addMentionedUsers(t: TweetType) returns TweetType
+  as "org.apache.asterix.external.library.AddMentionedUsersFactory" at testlib
+  with { "resources": {"textFieldName": "text"} };
 
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 913eab7..51254a5 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,12 +1,12 @@
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn001", "Arity": "0", "Params": [  ], "ReturnType": "any", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [  ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn002", "Arity": "1", "Params": [ "a" ], "ReturnType": "any", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "any" } ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn003", "Arity": "3", "Params": [ "a", "b", "c" ], "ReturnType": "string", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "string" }, { "Type": "$f$t$myfn003$3$1" }, { "Type": "$f$t$myfn003$3$2" } ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn004", "Arity": "2", "Params": [ "a", "b" ], "ReturnType": "CountryCapitalType", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "CountryCapitalType" }, { "Type": "$f$t$myfn004$2$1" } ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn005", "Arity": "4", "Params": [ "a", "b", "c", "d" ], "ReturnType": "string", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "int16" }, { "Type": "$f$t$myfn005$4$1" }, { "Type": "CountryCapitalType" }, { "Type": "$f$t$myfn005$4$3" } ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn006", "Arity": "1", "Params": [ "a" ], "ReturnType": "$f$t$myfn006$1", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "$f$t$myfn006$1$0" } ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn007", "Arity": "1", "Params": [ "a" ], "ReturnType": "$f$t$myfn007$1", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "$f$t$myfn007$1$0" } ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest", "Name": "myfn008", "Arity": "1", "Params": [ "a" ], "ReturnType": "$f$t$myfn008$1", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "$f$t$myfn008$1$0" } ], "Library": "testlib", "NullCall": false, "Deterministic": false } }
-{ "fn": { "DataverseName": "externallibtest2", "Name": "myfn009", "Arity": "1", "Params": [ "a" ], "ReturnType": "CountryCapitalType", "Definition": "org.apache.asterix.external.library.CapitalFinderFactory", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ReturnTypeDataverseName": "externallibtest", "ParamTypes": [ { "Type": "CountryCapitalType", "DataverseName": "externallibtest" } ], "Library": "testlib2", "NullCall": false, "Deterministic": false } }
+{ "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": "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" ], "NullCall": false, "Deterministic": true } }
+{ "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.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
+{ "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", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
+{ "fn": { "DataverseName": "externallibtest", "Name": "myfn006", "Arity": "1", "Params": [ "a" ], "ReturnType": "$f$t$myfn006$1", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "$f$t$myfn006$1$0" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
+{ "fn": { "DataverseName": "externallibtest", "Name": "myfn007", "Arity": "1", "Params": [ "a" ], "ReturnType": "$f$t$myfn007$1", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [  ] ], "ParamTypes": [ { "Type": "$f$t$myfn007$1$0" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
+{ "fn": { "DataverseName": "externallibtest", "Name": "myfn008", "Arity": "1", "Params": [ "a" ], "ReturnType": "$f$t$myfn008$1", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "$f$t$myfn008$1$0" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
+{ "fn": { "DataverseName": "externallibtest2", "Name": "myfn009", "Arity": "1", "Params": [ "a" ], "ReturnType": "CountryCapitalType", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ReturnTypeDataverseName": "externallibtest", "ParamTypes": [ { "Type": "CountryCapitalType", "DataverseName": "externallibtest" } ], "LibraryDataverseName": "externallibtest2", "LibraryName": "testlib2", "ExternalIdentifier": [ "org.apache.asterix.external.library.CapitalFinderFactory" ], "NullCall": false, "Deterministic": true } }
 { "dt": { "DataverseName": "externallibtest", "DatatypeName": "$f$t$myfn003$3$1", "Derived": { "Tag": "ORDEREDLIST", "IsAnonymous": true, "OrderedList": "int64" } } }
 { "dt": { "DataverseName": "externallibtest", "DatatypeName": "$f$t$myfn003$3$2", "Derived": { "Tag": "UNORDEREDLIST", "IsAnonymous": true, "UnorderedList": "boolean" } } }
 { "dt": { "DataverseName": "externallibtest", "DatatypeName": "$f$t$myfn004$2$1", "Derived": { "Tag": "ORDEREDLIST", "IsAnonymous": true, "OrderedList": "CountryCapitalType" } } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.4.adm
new file mode 100644
index 0000000..9c810dd
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.4.adm
@@ -0,0 +1,4 @@
+{ "tweetid": 1, "message-text": "1" }
+{ "tweetid": 2, "message-text": "2" }
+{ "tweetid": 3, "message-text": "3" }
+{ "tweetid": 4, "message-text": "4" }
\ No newline at end of file
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 156d5a0..f5b4ede 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
@@ -79,11 +79,6 @@
         <output-dir compare="Text">deterministic</output-dir>
       </compilation-unit>
     </test-case>
-    <test-case FilePath="feeds">
-      <compilation-unit name="feed-with-external-function">
-        <output-dir compare="Text">feed-with-external-function</output-dir>
-      </compilation-unit>
-    </test-case>
     <test-case FilePath="external-library">
       <compilation-unit name="return_invalid_type">
         <output-dir compare="Text">getCapital</output-dir>
@@ -107,5 +102,10 @@
         <output-dir compare="Text">feed-with-external-adapter</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="feeds">
+      <compilation-unit name="feed-with-external-adapter-cross-dv">
+        <output-dir compare="Text">feed-with-external-adapter-cross-dv</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
 </test-suite>
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index 87a11cc..00c7210 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -213,6 +213,11 @@
     public static final int COMPILATION_UNEXPECTED_ALIAS = 1120;
     public static final int COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE = 1121;
     public static final int COMPILATION_BAD_FUNCTION_DEFINITION = 1122;
+    public static final int FUNCTION_EXISTS = 1123;
+    public static final int ADAPTER_EXISTS = 1124;
+    public static final int UNKNOWN_ADAPTER = 1125;
+    public static final int INVALID_EXTERNAL_IDENTIFIER_SIZE = 1126;
+    public static final int UNSUPPORTED_ADAPTER_LANGUAGE = 1127;
 
     // Feed errors
     public static final int DATAFLOW_ILLEGAL_STATE = 3001;
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/metadata/IMetadataLockUtil.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/metadata/IMetadataLockUtil.java
index b788c1a..b3a7f54 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/metadata/IMetadataLockUtil.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/metadata/IMetadataLockUtil.java
@@ -83,7 +83,7 @@
     // Function helpers
 
     void createFunctionBegin(IMetadataLockManager lockManager, LockList locks, DataverseName dataverseName,
-            String functionName, String libraryName) throws AlgebricksException;
+            String functionName, DataverseName libraryDataverseName, String libraryName) throws AlgebricksException;
 
     void dropFunctionBegin(IMetadataLockManager lockManager, LockList locks, DataverseName dataverseName,
             String functionName) throws AlgebricksException;
@@ -91,7 +91,7 @@
     // Adapter helpers
 
     void createAdapterBegin(IMetadataLockManager lockManager, LockList locks, DataverseName dataverseName,
-            String adapterName, String libraryName) throws AlgebricksException;
+            String adapterName, DataverseName libraryDataverseName, String libraryName) throws AlgebricksException;
 
     void dropAdapterBegin(IMetadataLockManager lockManager, LockList locks, DataverseName dataverseName,
             String adapterName) throws AlgebricksException;
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index 438d719..8e761d9 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -208,6 +208,11 @@
 1120 = Unexpected alias: %1$s
 1121 = Illegal use of aggregate FILTER clause
 1122 = Error compiling function %1$s. %2$s
+1123 = A function with this name %1$s already exists
+1124 = An adapter with this name %1$s already exists
+1125 = Cannot find adapter with name %1$s
+1126 = Invalid number of elements in external identifier. Expected %1$s elements for %2$s language
+1127 = Unsupported adapter language: %1$s
 
 # Feed Errors
 3001 = Illegal state.
diff --git a/asterixdb/asterix-doc/src/main/user-defined_function/udf.md b/asterixdb/asterix-doc/src/main/user-defined_function/udf.md
index dc21c30..fcb1e14 100644
--- a/asterixdb/asterix-doc/src/main/user-defined_function/udf.md
+++ b/asterixdb/asterix-doc/src/main/user-defined_function/udf.md
@@ -71,15 +71,14 @@
 
 In the AsterixDB source release, we provide several sample UDFs that you can try out.
 You need to build the AsterixDB source to get the compiled UDF package. It can be found under
-the `asterixdb-external` sub-project. Assuming that these UDFs have been installed into the `udfs` dataverse and `testlib` library,
+the `asterixdb-external` sub-project. Assuming that these UDFs have been installed into the `testlib` library in`udfs` dataverse,
 here is an example that uses the sample UDF `mysum` to compute the sum of two input integers.
 
     USE udfs;
 
     CREATE FUNCTION mysum(a: int32, b: int32)
-    RETURNS int32
-    LANGUAGE JAVA
-    AS "testlib","org.apache.asterix.external.library.MySumFactory";
+      RETURNS int32
+      AS "org.apache.asterix.external.library.MySumFactory" AT testlib;
 
 ## <a id="PythonUDF">Creating a Python UDF</a>
 
@@ -118,7 +117,7 @@
 
     shiv -o lib.pyz --site-packages . scikit-learn
 
-Then, deploy it the same as the Java UDF was, with the library name `pylib`
+Then, deploy it the same as the Java UDF was, with the library name `pylib` in `udfs` dataverse
 
     curl -v -u admin:admin -X POST -F 'data=@./lib.pyz' localhost:19002/admin/udf/udfs/pylib
 
@@ -127,24 +126,31 @@
 
     USE udfs;
 
-    CREATE FUNCTION sentiment(a)
-    LANGUAGE PYTHON DETERMINISTIC
-    AS "pylib","sentiment_mod:sent_model";
+    CREATE FUNCTION sentiment(a) 
+      AS "sentiment_mod", "sent_model.sentiment" AT pylib;
 
-By default, AsterixDB will treat all external functions as `NOT DETERMINISTIC`. Loosely this means the result might
-change depending on when the function is called, regardless of the input. This function behaves the same on each input,
-so we can safely call it `DETERMINISTIC`. This will enable better optimization of queries including this function.
+By default, AsterixDB will treat all external functions as deterministic. It means the function must return the same
+result for the same input, irrespective of when or how many times the function is called on that input. 
+This particular function behaves the same on each input, so it satisfies the deterministic property. 
+This enables better optimization of queries including this function.
+If a function is not deterministic then it should be declared as such by using `WITH` sub-clause:
+
+    USE udfs;
+
+    CREATE FUNCTION sentiment(a)
+      AS "sentiment_mod", "sent_model.sentiment" AT pylib
+      WITH { "deterministic": false }
 
 With the function now defined, it can then be used as any other scalar SQL++ function would be. For example:
 
     USE udfs;
 
     INSERT INTO Tweets([
-    {"id":1, "msg":"spam is great"},
-    {"id":2, "msg":"i will not eat green eggs and ham"},
-    {"id":3, "msg":"bacon is better"}]);
+      {"id":1, "msg":"spam is great"},
+      {"id":2, "msg":"i will not eat green eggs and ham"},
+      {"id":3, "msg":"bacon is better"}
+    ]);
 
-    USE udfs;
     SELECT t.msg as msg, sentiment(t.msg) as sentiment
     FROM Tweets t;
 
@@ -191,12 +197,12 @@
 
 Then we define the function we want to apply to the feed
 
-   USE udfs;
+    USE udfs;
 
-   CREATE FUNCTION addMentionedUsers(t: TweetType)
-   RETURNS TweetType
-   LANGUAGE JAVA as "testlib","org.apache.asterix.external.library.AddMentionedUsersFactory"
-   WITH {"textFieldName": "text"};
+    CREATE FUNCTION addMentionedUsers(t: TweetType)
+      RETURNS TweetType
+      AS "org.apache.asterix.external.library.AddMentionedUsersFactory" AT testlib
+      WITH { "resources": { "textFieldName": "text" } };
 
 After creating the feed, we attach the UDF onto the feed pipeline and start the feed with following statements:
 
diff --git a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarJavaFunctionEvaluator.java b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarJavaFunctionEvaluator.java
index 4a073c9..33b0369 100755
--- a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarJavaFunctionEvaluator.java
+++ b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarJavaFunctionEvaluator.java
@@ -23,7 +23,6 @@
 
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.exceptions.RuntimeDataException;
-import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.external.api.IExternalScalarFunction;
 import org.apache.asterix.external.api.IFunctionFactory;
@@ -48,21 +47,20 @@
             IEvaluatorContext context) throws HyracksDataException {
         super(finfo, args, argTypes, context);
 
-        DataverseName functionDataverse = FunctionSignature.getDataverseName(finfo.getFunctionIdentifier());
-        String functionLibrary = finfo.getLibrary();
+        DataverseName libraryDataverseName = finfo.getLibraryDataverseName();
+        String libraryName = finfo.getLibraryName();
+        JavaLibrary library = (JavaLibrary) libraryManager.getLibrary(libraryDataverseName, libraryName);
 
-        functionHelper = new JavaFunctionHelper(finfo, argTypes, resultBuffer);
-        JavaLibrary lib = (JavaLibrary) libraryManager.getLibrary(functionDataverse, functionLibrary);
-        ClassLoader libraryClassLoader = lib.getClassLoader();
-        String classname = finfo.getExternalIdentifier().get(0).trim();
+        String classname = finfo.getExternalIdentifier().get(0);
         try {
-            Class<?> clazz = Class.forName(classname, true, libraryClassLoader);
+            Class<?> clazz = Class.forName(classname, true, library.getClassLoader());
             IFunctionFactory externalFunctionFactory = (IFunctionFactory) clazz.newInstance();
             externalFunctionInstance = (IExternalScalarFunction) externalFunctionFactory.getExternalFunction();
         } catch (Exception e) {
             throw new RuntimeDataException(ErrorCode.LIBRARY_EXTERNAL_FUNCTION_UNABLE_TO_LOAD_CLASS, e, classname);
         }
 
+        functionHelper = new JavaFunctionHelper(finfo, argTypes, resultBuffer);
         try {
             externalFunctionInstance.initialize(functionHelper);
         } catch (Exception e) {
diff --git a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarPythonFunctionEvaluator.java b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarPythonFunctionEvaluator.java
index 31f96cf..c47145b 100644
--- a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarPythonFunctionEvaluator.java
+++ b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/ExternalScalarPythonFunctionEvaluator.java
@@ -81,8 +81,8 @@
         File pythonPath = new File(ctx.getServiceContext().getAppConfig().getString(NCConfig.Option.PYTHON_HOME));
         DataverseName dataverseName = FunctionSignature.getDataverseName(finfo.getFunctionIdentifier());
         try {
-            libraryEvaluator = PythonLibraryEvaluator.getInstance(dataverseName, finfo, libraryManager, router, ipcSys,
-                    pythonPath, ctx.getTaskContext(), ctx.getWarningCollector(), sourceLoc);
+            libraryEvaluator = PythonLibraryEvaluator.getInstance(finfo, libraryManager, router, ipcSys, pythonPath,
+                    ctx.getTaskContext(), ctx.getWarningCollector(), sourceLoc);
         } catch (IOException | AsterixException e) {
             throw new HyracksDataException("Failed to initialize Python", e);
         }
@@ -150,16 +150,19 @@
         public void initialize() throws IOException, AsterixException {
             PythonLibraryEvaluatorId fnId = (PythonLibraryEvaluatorId) id;
             List<String> externalIdents = finfo.getExternalIdentifier();
-            PythonLibrary library = (PythonLibrary) libMgr.getLibrary(fnId.dataverseName, fnId.libraryName);
+            PythonLibrary library = (PythonLibrary) libMgr.getLibrary(fnId.libraryDataverseName, fnId.libraryName);
             String wd = library.getFile().getAbsolutePath();
             String packageModule = externalIdents.get(0);
-            String clazz = "None";
+            String clazz;
             String fn;
-            if (externalIdents.size() > 2) {
-                clazz = externalIdents.get(1);
-                fn = externalIdents.get(2);
+            String externalIdent1 = externalIdents.get(1);
+            int idx = externalIdent1.lastIndexOf('.');
+            if (idx >= 0) {
+                clazz = externalIdent1.substring(0, idx);
+                fn = externalIdent1.substring(idx + 1);
             } else {
-                fn = externalIdents.get(1);
+                clazz = "None";
+                fn = externalIdent1;
             }
             this.fn = fn;
             this.clazz = clazz;
@@ -188,23 +191,25 @@
 
         @Override
         public void deallocate() {
-            boolean dead = false;
-            try {
-                p.destroy();
-                dead = p.waitFor(100, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
-                //gonna kill it anyway
-            }
-            if (!dead) {
-                p.destroyForcibly();
+            if (p != null) {
+                boolean dead = false;
+                try {
+                    p.destroy();
+                    dead = p.waitFor(100, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException e) {
+                    //gonna kill it anyway
+                }
+                if (!dead) {
+                    p.destroyForcibly();
+                }
             }
         }
 
-        private static PythonLibraryEvaluator getInstance(DataverseName dataverseName, IExternalFunctionInfo finfo,
-                ILibraryManager libMgr, ExternalFunctionResultRouter router, IPCSystem ipcSys, File pythonHome,
-                IHyracksTaskContext ctx, IWarningCollector warningCollector, SourceLocation sourceLoc)
-                throws IOException, AsterixException {
-            PythonLibraryEvaluatorId evaluatorId = new PythonLibraryEvaluatorId(dataverseName, finfo.getLibrary());
+        private static PythonLibraryEvaluator getInstance(IExternalFunctionInfo finfo, ILibraryManager libMgr,
+                ExternalFunctionResultRouter router, IPCSystem ipcSys, File pythonHome, IHyracksTaskContext ctx,
+                IWarningCollector warningCollector, SourceLocation sourceLoc) throws IOException, AsterixException {
+            PythonLibraryEvaluatorId evaluatorId =
+                    new PythonLibraryEvaluatorId(finfo.getLibraryDataverseName(), finfo.getLibraryName());
             PythonLibraryEvaluator evaluator = (PythonLibraryEvaluator) ctx.getStateObject(evaluatorId);
             if (evaluator == null) {
                 evaluator = new PythonLibraryEvaluator(ctx.getJobletContext().getJobId(), evaluatorId, finfo, libMgr,
@@ -219,12 +224,12 @@
 
     private static final class PythonLibraryEvaluatorId {
 
-        private final DataverseName dataverseName;
+        private final DataverseName libraryDataverseName;
 
         private final String libraryName;
 
-        private PythonLibraryEvaluatorId(DataverseName dataverseName, String libraryName) {
-            this.dataverseName = Objects.requireNonNull(dataverseName);
+        private PythonLibraryEvaluatorId(DataverseName libraryDataverseName, String libraryName) {
+            this.libraryDataverseName = Objects.requireNonNull(libraryDataverseName);
             this.libraryName = Objects.requireNonNull(libraryName);
         }
 
@@ -235,12 +240,12 @@
             if (o == null || getClass() != o.getClass())
                 return false;
             PythonLibraryEvaluatorId that = (PythonLibraryEvaluatorId) o;
-            return dataverseName.equals(that.dataverseName) && libraryName.equals(that.libraryName);
+            return libraryDataverseName.equals(that.libraryDataverseName) && libraryName.equals(that.libraryName);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(dataverseName, libraryName);
+            return Objects.hash(libraryDataverseName, libraryName);
         }
     }
 
diff --git a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/JavaFunctionHelper.java b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/JavaFunctionHelper.java
index 5f6e1eb..d1b2685 100644
--- a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/JavaFunctionHelper.java
+++ b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/library/JavaFunctionHelper.java
@@ -67,14 +67,14 @@
         this.outputProvider = outputProvider;
         this.pointableVisitor = new JObjectPointableVisitor();
         this.pointableAllocator = new PointableAllocator();
-        this.arguments = new IJObject[finfo.getArgumentList().size()];
+        this.arguments = new IJObject[finfo.getParameterTypes().size()];
         int index = 0;
-        for (IAType param : finfo.getArgumentList()) {
+        for (IAType param : finfo.getParameterTypes()) {
             this.arguments[index++] = objectPool.allocate(param);
         }
         this.resultHolder = objectPool.allocate(finfo.getReturnType());
         this.poolTypeInfo = new HashMap<>();
-        this.parameters = finfo.getParams();
+        this.parameters = finfo.getResources();
         this.argTypes = argTypes;
 
     }
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/AdapterDropStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/AdapterDropStatement.java
index b2733ab..481dbe4 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/AdapterDropStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/AdapterDropStatement.java
@@ -19,17 +19,21 @@
 package org.apache.asterix.lang.common.statement;
 
 import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.external.dataset.adapter.AdapterIdentifier;
+import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.lang.common.base.AbstractStatement;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 
 public class AdapterDropStatement extends AbstractStatement {
 
-    private final AdapterIdentifier signature;
-    private boolean ifExists;
+    private final DataverseName dataverseName;
 
-    public AdapterDropStatement(AdapterIdentifier signature, boolean ifExists) {
-        this.signature = signature;
+    private final String adapterName;
+
+    private final boolean ifExists;
+
+    public AdapterDropStatement(DataverseName dataverseName, String adapterName, boolean ifExists) {
+        this.dataverseName = dataverseName;
+        this.adapterName = adapterName;
         this.ifExists = ifExists;
     }
 
@@ -38,8 +42,12 @@
         return Kind.ADAPTER_DROP;
     }
 
-    public AdapterIdentifier getAdapterIdentifier() {
-        return signature;
+    public DataverseName getDataverseName() {
+        return dataverseName;
+    }
+
+    public String getAdapterName() {
+        return adapterName;
     }
 
     public boolean getIfExists() {
@@ -55,5 +63,4 @@
     public byte getCategory() {
         return Category.DDL;
     }
-
 }
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateAdapterStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateAdapterStatement.java
index 4741f8b..6e5886c 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateAdapterStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateAdapterStatement.java
@@ -18,28 +18,35 @@
  */
 package org.apache.asterix.lang.common.statement;
 
+import java.util.List;
+
 import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.external.dataset.adapter.AdapterIdentifier;
+import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.lang.common.base.AbstractStatement;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 
 public class CreateAdapterStatement extends AbstractStatement {
 
-    private final AdapterIdentifier signature;
+    private final DataverseName dataverseName;
 
-    String lang;
-    String libName;
-    String externalIdent;
-    boolean ifNotExists;
+    private final String adapterName;
 
-    public CreateAdapterStatement(AdapterIdentifier signature, String lang, String libName, String externalIdent,
-            boolean ifNotExists) {
-        this.signature = signature;
-        this.lang = lang;
-        this.libName = libName;
-        this.externalIdent = externalIdent;
+    private final DataverseName libraryDataverseName;
+
+    private final String libraryName;
+
+    private final List<String> externalIdentifier;
+
+    private final boolean ifNotExists;
+
+    public CreateAdapterStatement(DataverseName dataverseName, String adapterName, DataverseName libraryDataverseName,
+            String libraryName, List<String> externalIdentifier, boolean ifNotExists) {
+        this.dataverseName = dataverseName;
+        this.adapterName = adapterName;
+        this.libraryDataverseName = libraryDataverseName;
+        this.libraryName = libraryName;
+        this.externalIdentifier = externalIdentifier;
         this.ifNotExists = ifNotExists;
-
     }
 
     @Override
@@ -47,20 +54,28 @@
         return Kind.CREATE_ADAPTER;
     }
 
-    public AdapterIdentifier getAdapterId() {
-        return signature;
+    public DataverseName getDataverseName() {
+        return dataverseName;
     }
 
-    public String getLibName() {
-        return libName;
+    public String getAdapterName() {
+        return adapterName;
     }
 
-    public String getExternalIdent() {
-        return externalIdent;
+    public DataverseName getLibraryDataverseName() {
+        return libraryDataverseName;
     }
 
-    public String getLang() {
-        return lang;
+    public String getLibraryName() {
+        return libraryName;
+    }
+
+    public List<String> getExternalIdentifier() {
+        return externalIdentifier;
+    }
+
+    public boolean getIfNotExists() {
+        return ifNotExists;
     }
 
     @Override
@@ -72,5 +87,4 @@
     public byte getCategory() {
         return Category.DDL;
     }
-
 }
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 082665f..2e8a937 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
@@ -18,12 +18,13 @@
  */
 package org.apache.asterix.lang.common.statement;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 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.common.metadata.DataverseName;
 import org.apache.asterix.lang.common.base.AbstractStatement;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.Statement;
@@ -33,11 +34,21 @@
 import org.apache.asterix.lang.common.util.ConfigurationUtil;
 import org.apache.asterix.lang.common.util.ExpressionUtils;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
+import org.apache.asterix.object.base.AdmBooleanNode;
 import org.apache.asterix.object.base.AdmObjectNode;
+import org.apache.asterix.object.base.AdmStringNode;
+import org.apache.asterix.object.base.IAdmNode;
+import org.apache.asterix.om.types.ATypeTag;
 import org.apache.hyracks.algebricks.common.utils.Pair;
 
 public class CreateFunctionStatement extends AbstractStatement {
 
+    private static final String NULLCALL_FIELD_NAME = "null-call";
+    private static final boolean NULLCALL_DEFAULT = false;
+    private static final String DETERMINISTIC_FIELD_NAME = "deterministic";
+    private static final boolean DETERMINISTIC_DEFAULT = true;
+    private static final String RESOURCES_FIELD_NAME = "resources";
+
     private final FunctionSignature signature;
     private final String functionBody;
     private final Expression functionBodyExpression;
@@ -45,12 +56,10 @@
     private final List<Pair<VarIdentifier, TypeExpression>> paramList;
     private final TypeExpression returnType;
 
-    private final String lang;
-    private final String libName;
+    private final DataverseName libraryDataverseName;
+    private final String libraryName;
     private final List<String> externalIdentifier;
-    private final Boolean deterministic;
-    private final Boolean nullCall;
-    private final AdmObjectNode resources;
+    private final AdmObjectNode options;
 
     public CreateFunctionStatement(FunctionSignature signature, List<Pair<VarIdentifier, TypeExpression>> paramList,
             String functionBody, Expression functionBodyExpression, boolean ifNotExists) {
@@ -58,30 +67,26 @@
         this.functionBody = functionBody;
         this.functionBodyExpression = functionBodyExpression;
         this.ifNotExists = ifNotExists;
-        this.paramList = requireNullTypes(paramList);
+        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.lang = null;
-        this.libName = null;
+        this.libraryDataverseName = null;
+        this.libraryName = null;
         this.externalIdentifier = null;
-        this.deterministic = null;
-        this.nullCall = null;
-        this.resources = null;
+        this.options = null;
     }
 
     public CreateFunctionStatement(FunctionSignature signature, List<Pair<VarIdentifier, TypeExpression>> paramList,
-            TypeExpression returnType, boolean deterministic, boolean nullCall, String lang, String libName,
-            List<String> externalIdentifier, RecordConstructor resources, boolean ifNotExists)
+            TypeExpression returnType, DataverseName libraryDataverseName, String libraryName,
+            List<String> externalIdentifier, RecordConstructor options, boolean ifNotExists)
             throws CompilationException {
         this.signature = signature;
         this.ifNotExists = ifNotExists;
         this.paramList = paramList;
         this.returnType = returnType;
-        this.deterministic = deterministic;
-        this.nullCall = nullCall;
-        this.lang = lang;
-        this.libName = libName;
+        this.libraryDataverseName = libraryDataverseName;
+        this.libraryName = libraryName;
         this.externalIdentifier = externalIdentifier;
-        this.resources = resources == null ? null : ExpressionUtils.toNode(resources);
+        this.options = options == null ? null : ExpressionUtils.toNode(options);
         this.functionBody = null;
         this.functionBodyExpression = null;
     }
@@ -123,24 +128,50 @@
         return externalIdentifier;
     }
 
-    public String getLibName() {
-        return libName;
+    public DataverseName getLibraryDataverseName() {
+        return libraryDataverseName;
     }
 
-    public String getLang() {
-        return lang;
+    public String getLibraryName() {
+        return libraryName;
     }
 
-    public Boolean getDeterministic() {
-        return deterministic;
+    public boolean getNullCall() throws CompilationException {
+        Boolean nullCall = getBooleanOption(NULLCALL_FIELD_NAME);
+        return nullCall != null ? nullCall : NULLCALL_DEFAULT;
     }
 
-    public Boolean getNullCall() {
-        return nullCall;
+    public boolean getDeterministic() throws CompilationException {
+        Boolean deterministic = getBooleanOption(DETERMINISTIC_FIELD_NAME);
+        return deterministic != null ? deterministic : DETERMINISTIC_DEFAULT;
+    }
+
+    private Boolean getBooleanOption(String optionName) throws CompilationException {
+        IAdmNode value = getOption(optionName);
+        if (value == null) {
+            return null;
+        }
+        switch (value.getType()) {
+            case BOOLEAN:
+                return ((AdmBooleanNode) value).get();
+            case STRING:
+                return Boolean.parseBoolean(((AdmStringNode) value).get());
+            default:
+                throw new CompilationException(ErrorCode.FIELD_NOT_OF_TYPE, getSourceLocation(), optionName,
+                        ATypeTag.BOOLEAN, value.getType());
+        }
     }
 
     public Map<String, String> getResources() throws CompilationException {
-        return resources != null ? ConfigurationUtil.toProperties(resources) : Collections.emptyMap();
+        IAdmNode value = getOption(RESOURCES_FIELD_NAME);
+        if (value == null) {
+            return null;
+        }
+        if (value.getType() != ATypeTag.OBJECT) {
+            throw new CompilationException(ErrorCode.FIELD_NOT_OF_TYPE, getSourceLocation(), RESOURCES_FIELD_NAME,
+                    ATypeTag.OBJECT, value.getType());
+        }
+        return ConfigurationUtil.toProperties((AdmObjectNode) value);
     }
 
     @Override
@@ -153,6 +184,10 @@
         return Category.DDL;
     }
 
+    private IAdmNode getOption(String optionName) {
+        return options != null ? options.get(optionName) : null;
+    }
+
     private static List<Pair<VarIdentifier, TypeExpression>> requireNullTypes(
             List<Pair<VarIdentifier, TypeExpression>> paramList) {
         for (Pair<VarIdentifier, TypeExpression> p : paramList) {
@@ -162,4 +197,5 @@
         }
         return paramList;
     }
+
 }
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/LibraryDropStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/LibraryDropStatement.java
index 09ea3a8..ae8e90c 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/LibraryDropStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/LibraryDropStatement.java
@@ -29,9 +29,12 @@
     private final DataverseName dataverseName;
     private final String libraryName;
 
-    public LibraryDropStatement(DataverseName dataverseName, String libraryName) {
+    private final boolean ifExists;
+
+    public LibraryDropStatement(DataverseName dataverseName, String libraryName, boolean ifExists) {
         this.dataverseName = dataverseName;
         this.libraryName = libraryName;
+        this.ifExists = ifExists;
     }
 
     public DataverseName getDataverseName() {
@@ -42,6 +45,10 @@
         return libraryName;
     }
 
+    public boolean getIfExists() {
+        return ifExists;
+    }
+
     @Override
     public Kind getKind() {
         return Kind.LIBRARY_DROP;
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 62763d1..c093005 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
@@ -33,7 +33,6 @@
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.external.dataset.adapter.AdapterIdentifier;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.Literal;
 import org.apache.asterix.lang.common.clause.LetClause;
@@ -818,7 +817,7 @@
     @Override
     public Void visit(CreateAdapterStatement cfs, Integer step) throws CompilationException {
         out.print(skip(step) + CREATE + " adapter");
-        out.print(this.generateFullName(cfs.getAdapterId().getDataverseName(), cfs.getAdapterId().getName()));
+        out.print(this.generateFullName(cfs.getDataverseName(), cfs.getAdapterName()));
         out.println(SEMICOLON);
         out.println();
         return null;
@@ -827,9 +826,8 @@
     @Override
     public Void visit(AdapterDropStatement del, Integer step) throws CompilationException {
         out.print(skip(step) + "drop adapter ");
-        AdapterIdentifier funcSignature = del.getAdapterIdentifier();
-        out.print(funcSignature.toString());
-        out.println(SEMICOLON);
+        out.print(generateFullName(del.getDataverseName(), del.getAdapterName()));
+        out.println(generateIfExists(del.getIfExists()) + SEMICOLON);
         return null;
     }
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index e1b93b9..d2c851f 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -118,6 +118,7 @@
 import org.apache.asterix.lang.common.literal.StringLiteral;
 import org.apache.asterix.lang.common.literal.TrueLiteral;
 import org.apache.asterix.lang.common.parser.ScopeChecker;
+import org.apache.asterix.lang.common.statement.AdapterDropStatement;
 import org.apache.asterix.lang.common.statement.CompactStatement;
 import org.apache.asterix.lang.common.statement.ConnectFeedStatement;
 import org.apache.asterix.lang.common.statement.StartFeedStatement;
@@ -231,13 +232,7 @@
     private static final String SETS = "SETS";
     private static final String TIES = "TIES";
     private static final String UNBOUNDED = "UNBOUNDED";
-    private static final String ACTION = "ACTION";
-    private static final String LANGUAGE = "LANGUAGE";
-    private static final String CALL = "CALL";
-    private static final String DETERMINISTIC = "DETERMINISTIC";
     private static final String RETURNS = "RETURNS";
-    private static final String INLINE = "INLINE";
-
 
     private static final String INT_TYPE_NAME = "int";
 
@@ -1100,18 +1095,6 @@
   }
 }
 
-CreateFunctionStatement CreateFunctionStatement(Token startStmtToken) throws ParseException:
-{
-  CreateFunctionStatement stmt = null;
-}
-{
-  <FUNCTION> stmt = FunctionSpecification(startStmtToken)
-  {
-    return stmt;
-  }
-}
-
-
 CreateAdapterStatement CreateAdapterStatement(Token startStmtToken) throws ParseException:
 {
   CreateAdapterStatement stmt = null;
@@ -1125,49 +1108,48 @@
 
 CreateAdapterStatement AdapterSpecification(Token startStmtToken) throws ParseException:
 {
-  AdapterIdentifier signature = null;
-  Token beginPos;
-  Token endPos;
-  Pair<DataverseName,Identifier> adaptName = null;
-  DataverseName currentDataverse = defaultDataverse;
-  String lang = null;
-  String libName ="";
-  String externalIdent = "";
-  ListConstructor resources = null;
+  Pair<DataverseName,Identifier> adapterName = null;
+  Pair<DataverseName,Identifier> libraryName = null;
+  List<String> externalIdentifier = null;
   boolean ifNotExists = false;
 }
 {
-  adaptName = QualifiedName()
-  <IDENTIFIER> {expectToken(LANGUAGE);} lang = Identifier() <AS> libName = ConstantString() <COMMA>  externalIdent = ConstantString() ifNotExists = IfNotExists()
-    {
-      signature = new AdapterIdentifier(adaptName.getFirst(),adaptName.getSecond().getValue());
-      CreateAdapterStatement stmt =
-        new CreateAdapterStatement(signature, lang, libName, externalIdent,  ifNotExists);
-      return addSourceLocation(stmt, startStmtToken);
-    }
+  adapterName = QualifiedName()
+  ifNotExists = IfNotExists()
+  <AS> externalIdentifier = FunctionExternalIdentifier()
+  <AT> libraryName = QualifiedName()
+  {
+    CreateAdapterStatement stmt = new CreateAdapterStatement(adapterName.first, adapterName.second.getValue(),
+      libraryName.first, libraryName.second.getValue(), externalIdentifier, ifNotExists);
+    return addSourceLocation(stmt, startStmtToken);
+  }
+}
 
+CreateFunctionStatement CreateFunctionStatement(Token startStmtToken) throws ParseException:
+{
+  CreateFunctionStatement stmt = null;
+}
+{
+  <FUNCTION> stmt = FunctionSpecification(startStmtToken)
+  {
+    return stmt;
+  }
 }
 
 CreateFunctionStatement FunctionSpecification(Token startStmtToken) throws ParseException:
 {
   FunctionSignature signature = null;
-  boolean ifNotExists = false;
-  String functionBody = null;
-  VarIdentifier var = null;
-  Expression functionBodyExpr = null;
-  Token beginPos;
-  Token endPos;
   FunctionName fctName = null;
-  DataverseName currentDataverse = defaultDataverse;
-  TypeExpression returnType = null;
-  boolean deterministic = false;
-  boolean nullCall = false;
-  String lang = null;
-  String libName ="";
-  String externalIdent = "";
-  List<String> externalIdentList = null;
   List<Pair<VarIdentifier,TypeExpression>> params = null;
-  RecordConstructor resources = null;
+  TypeExpression returnType = null;
+  Token beginPos = null, endPos = null;
+  Expression functionBodyExpr = null;
+  Pair<DataverseName,Identifier> libraryName = null;
+  List<String> externalIdentifier = null;
+  RecordConstructor withOptions = null;
+  boolean ifNotExists = false;
+  CreateFunctionStatement stmt = null;
+  DataverseName currentDataverse = defaultDataverse;
 }
 {
   fctName = FunctionName()
@@ -1181,69 +1163,41 @@
     (
       <LEFTBRACE>
       {
-          createNewScope();
-          beginPos = token;
+        beginPos = token;
+        createNewScope();
       }
       functionBodyExpr = FunctionBody()
-      <RIGHTBRACE>{
-          endPos = token;
-          functionBody = extractFragment(beginPos.beginLine, beginPos.beginColumn, endPos.beginLine, endPos.beginColumn);
-          signature = new FunctionSignature(fctName.dataverse, fctName.function, params.size());
-          getCurrentScope().addFunctionDescriptor(signature, false);
-          removeCurrentScope();
-          defaultDataverse = currentDataverse;
-          ensureNoTypeDeclsInFunction(fctName.function, params, returnType, startStmtToken);
-          CreateFunctionStatement stmt = new CreateFunctionStatement(signature, params, functionBody, functionBodyExpr, ifNotExists);
-          return addSourceLocation(stmt, startStmtToken);
-        }
+      <RIGHTBRACE>
+      {
+        endPos = token;
+        String functionBody = extractFragment(beginPos.beginLine, beginPos.beginColumn, endPos.beginLine,
+          endPos.beginColumn);
+        signature = new FunctionSignature(fctName.dataverse, fctName.function, params.size());
+        getCurrentScope().addFunctionDescriptor(signature, false);
+        removeCurrentScope();
+        defaultDataverse = currentDataverse;
+        ensureNoTypeDeclsInFunction(fctName.function, params, returnType, startStmtToken);
+        stmt = new CreateFunctionStatement(signature, params, functionBody, functionBodyExpr, ifNotExists);
+        return addSourceLocation(stmt, startStmtToken);
+      }
     )
   |
-     <IDENTIFIER> {expectToken(LANGUAGE);}
-     (
-        LOOKAHEAD({laIdentifier(INLINE)})
-        (
-              <IDENTIFIER> <AS>
-              {
-                  createNewScope();
-                  beginPos = token;
-              }
-              functionBodyExpr = FunctionBody()
-              {
-                  endPos = token;
-                  functionBody = extractFragment(beginPos.endLine, beginPos.beginColumn+1, endPos.endLine, endPos.endColumn+1);
-                  signature = new FunctionSignature(fctName.dataverse, fctName.function, params.size());
-                  getCurrentScope().addFunctionDescriptor(signature, false);
-                  removeCurrentScope();
-                  defaultDataverse = currentDataverse;
-                  ensureNoTypeDeclsInFunction(fctName.function, params, returnType, startStmtToken);
-                  CreateFunctionStatement stmt = new CreateFunctionStatement(signature, params, functionBody, functionBodyExpr, ifNotExists);
-                  return addSourceLocation(stmt, startStmtToken);
-              }
-        )
-        |
-        (
-              lang = Identifier()
-              ( (<NOT> <IDENTIFIER> {expectToken(DETERMINISTIC); deterministic = false;}) | (<IDENTIFIER> {expectToken(DETERMINISTIC); deterministic = true;}) )?
-              (<NULL> <IDENTIFIER> { expectToken(CALL); nullCall = true;})?
-              <AS> libName = ConstantString()
-              { externalIdentList = new ArrayList<String>(2); }
-              ( <COMMA>  externalIdent = ConstantString() { externalIdentList.add(externalIdent); } )+
-              (<WITH> resources = RecordConstructor())?
-              {
-                  signature = new FunctionSignature(fctName.dataverse, fctName.function, params.size());
-                  defaultDataverse = currentDataverse;
-                  CreateFunctionStatement stmt = null;
-                  try{
-                      stmt =
-                      new CreateFunctionStatement(signature, params, returnType, deterministic, nullCall,
-                                                   lang, libName, externalIdentList, resources,  ifNotExists);
-                  } catch (AlgebricksException e) {
-                      throw new SqlppParseException(getSourceLocation(startStmtToken), e.getMessage());
-                  }
-                  return addSourceLocation(stmt, startStmtToken);
-              }
-        )
-     )
+    (
+      <AS> externalIdentifier = FunctionExternalIdentifier()
+      <AT> libraryName = QualifiedName()
+      (<WITH> withOptions = RecordConstructor())?
+      {
+        signature = new FunctionSignature(fctName.dataverse, fctName.function, params.size());
+        defaultDataverse = currentDataverse;
+        try {
+          stmt = new CreateFunctionStatement(signature, params, returnType, libraryName.first,
+            libraryName.second.getValue(), externalIdentifier, withOptions, ifNotExists);
+        } catch (AlgebricksException e) {
+            throw new SqlppParseException(getSourceLocation(startStmtToken), e.getMessage());
+        }
+        return addSourceLocation(stmt, startStmtToken);
+      }
+    )
   )
 }
 
@@ -1306,6 +1260,19 @@
   }
 }
 
+List<String> FunctionExternalIdentifier() throws ParseException:
+{
+  String ident = null;
+  List<String> identList = new ArrayList(2);
+}
+{
+  ident = StringLiteral() { identList.add(ident.trim()); }
+  ( <COMMA> ident = StringLiteral() { identList.add(ident.trim()); } )*
+  {
+    return identList;
+  }
+}
+
 CreateFeedStatement CreateFeedStatement(Token startStmtToken) throws ParseException:
 {
   CreateFeedStatement stmt = null;
@@ -1507,6 +1474,7 @@
     | stmt = DropNodeGroupStatement(startToken)
     | stmt = DropTypeStatement(startToken)
     | stmt = DropDataverseStatement(startToken)
+    | stmt = DropAdapterStatement(startToken)
     | stmt = DropFunctionStatement(startToken)
     | stmt = DropFeedStatement(startToken)
     | stmt = DropFeedPolicyStatement(startToken)
@@ -1637,6 +1605,30 @@
   }
 }
 
+AdapterDropStatement DropAdapterStatement(Token startStmtToken) throws ParseException:
+{
+  AdapterDropStatement stmt = null;
+}
+{
+  <ADAPTER> stmt = DropAdapterSpecification(startStmtToken)
+  {
+    return stmt;
+  }
+}
+
+AdapterDropStatement DropAdapterSpecification(Token startStmtToken) throws ParseException:
+{
+  Pair<DataverseName,Identifier> adapterName = null;
+  boolean ifExists = false;
+}
+{
+  adapterName = QualifiedName() ifExists = IfExists()
+  {
+    AdapterDropStatement stmt = new AdapterDropStatement(adapterName.first, adapterName.second.getValue(), ifExists);
+    return addSourceLocation(stmt, startStmtToken);
+  }
+}
+
 FunctionDropStatement DropFunctionStatement(Token startStmtToken) throws ParseException:
 {
   FunctionDropStatement stmt = null;
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 169e436..07dd4e5 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
@@ -1061,7 +1061,8 @@
             throws AlgebricksException {
         List<Function> functions = getAllFunctions(txnId);
         for (Function function : functions) {
-            if (libraryName.equals(function.getLibrary()) && dataverseName.equals(function.getDataverseName())) {
+            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());
@@ -1074,7 +1075,7 @@
         List<DatasourceAdapter> adapters = getAllAdapters(txnId);
         for (DatasourceAdapter adapter : adapters) {
             if (libraryName.equals(adapter.getLibraryName())
-                    && adapter.getAdapterIdentifier().getDataverseName().equals(dataverseName)) {
+                    && dataverseName.equals(adapter.getLibraryDataverseName())) {
                 throw new AlgebricksException("Cannot drop library " + dataverseName + '.' + libraryName
                         + " being used by adapter " + adapter.getAdapterIdentifier().getDataverseName() + '.'
                         + adapter.getAdapterIdentifier().getName());
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java
index 90fe3a5..0bf8c3d 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java
@@ -158,14 +158,15 @@
     }
 
     public void dropFunction(FunctionSignature signature) {
-        Function function = new Function(signature, null, null, null, null, null, null, null, false, false, null, null);
+        Function function =
+                new Function(signature, null, null, null, null, null, null, null, null, null, false, false, null, null);
         droppedCache.addFunctionIfNotExists(function);
         logAndApply(new MetadataLogicalOperation(function, false));
     }
 
     public void dropAdapter(DataverseName dataverseName, String adapterName) {
         AdapterIdentifier adapterIdentifier = new AdapterIdentifier(dataverseName, adapterName);
-        DatasourceAdapter adapter = new DatasourceAdapter(adapterIdentifier, null, null);
+        DatasourceAdapter adapter = new DatasourceAdapter(adapterIdentifier, null, null, null, null);
         droppedCache.addAdapterIfNotExists(adapter);
         logAndApply(new MetadataLogicalOperation(adapter, false));
     }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java
index d85422d..4a64b8a 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java
@@ -318,7 +318,7 @@
             String adapterName =
                     ((ITypedAdapterFactory) (Class.forName(adapterFactoryClassName).newInstance())).getAlias();
             return new DatasourceAdapter(new AdapterIdentifier(MetadataConstants.METADATA_DATAVERSE_NAME, adapterName),
-                    adapterFactoryClassName, IDataSourceAdapter.AdapterType.INTERNAL);
+                    IDataSourceAdapter.AdapterType.INTERNAL, adapterFactoryClassName, null, null);
         } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
             throw new MetadataException("Unable to instantiate builtin Adapter", e);
         }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java
index a1d6743..8430f44 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java
@@ -75,6 +75,7 @@
     public static final String FIELD_NAME_IS_PRIMARY = "IsPrimary";
     public static final String FIELD_NAME_KIND = "Kind";
     public static final String FIELD_NAME_LANGUAGE = "Language";
+    public static final String FIELD_NAME_LIBRARY_DATAVERSE_NAME = "LibraryDataverseName";
     public static final String FIELD_NAME_LIBRARY_NAME = "LibraryName";
     public static final String FIELD_NAME_LAST_REFRESH_TIME = "LastRefreshTime";
     public static final String FIELD_NAME_METATYPE_DATAVERSE_NAME = "MetatypeDataverseName";
@@ -340,11 +341,15 @@
     public static final int FUNCTION_ARECORD_FUNCTION_KIND_FIELD_INDEX = 7;
     public static final int FUNCTION_ARECORD_FUNCTION_DEPENDENCIES_FIELD_INDEX = 8;
     //open types
-    public static final String FUNCTION_ARECORD_FUNCTION_WITHPARAM_LIST_NAME = "WithParams";
-    public static final String FUNCTION_ARECORD_FUNCTION_LIBRARY_FIELD_NAME = "Library";
+    public static final String FUNCTION_ARECORD_FUNCTION_RESOURCES_FIELD_NAME = "Resources";
     public static final String FUNCTION_ARECORD_FUNCTION_NULLCALL_FIELD_NAME = "NullCall";
     public static final String FUNCTION_ARECORD_FUNCTION_DETERMINISTIC_FIELD_NAME = "Deterministic";
     public static final String FUNCTION_ARECORD_FUNCTION_PARAMTYPES_FIELD_NAME = "ParamTypes";
+    public static final String FUNCTION_ARECORD_FUNCTION_EXTERNAL_IDENTIFIER_FIELD_NAME = "ExternalIdentifier";
+    @Deprecated // back-compat
+    public static final String FUNCTION_ARECORD_FUNCTION_LIBRARY_FIELD_NAME = "Library";
+    @Deprecated // back-compat
+    public static final String FUNCTION_ARECORD_FUNCTION_WITHPARAMS_FIELD_NAME = "WithParams";
 
     public static final ARecordType FUNCTION_RECORDTYPE = createRecordType(
             // RecordTypeName
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/DatasourceAdapter.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/DatasourceAdapter.java
index 5e0ece3..eedfe8d 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/DatasourceAdapter.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/DatasourceAdapter.java
@@ -19,32 +19,27 @@
 package org.apache.asterix.metadata.entities;
 
 import org.apache.asterix.common.external.IDataSourceAdapter.AdapterType;
+import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.external.dataset.adapter.AdapterIdentifier;
 import org.apache.asterix.metadata.MetadataCache;
 import org.apache.asterix.metadata.api.IMetadataEntity;
 
 public class DatasourceAdapter implements IMetadataEntity<DatasourceAdapter> {
 
-    private static final long serialVersionUID = 2L;
+    private static final long serialVersionUID = 3L;
 
     private final AdapterIdentifier adapterIdentifier;
     private final String classname;
     private final AdapterType type;
-    //TODO:also need libraryDataverse
+    private final DataverseName libraryDataverseName;
     private final String libraryName;
 
-    public DatasourceAdapter(AdapterIdentifier adapterIdentifier, String classname, AdapterType type) {
+    public DatasourceAdapter(AdapterIdentifier adapterIdentifier, AdapterType type, String classname,
+            DataverseName libraryDataverseName, String libraryName) {
         this.adapterIdentifier = adapterIdentifier;
-        this.classname = classname;
         this.type = type;
-        this.libraryName = null;
-    }
-
-    public DatasourceAdapter(AdapterIdentifier adapterIdentifier, String classname, AdapterType type,
-            String libraryName) {
-        this.adapterIdentifier = adapterIdentifier;
         this.classname = classname;
-        this.type = type;
+        this.libraryDataverseName = libraryDataverseName;
         this.libraryName = libraryName;
     }
 
@@ -70,6 +65,10 @@
         return type;
     }
 
+    public DataverseName getLibraryDataverseName() {
+        return libraryDataverseName;
+    }
+
     public String getLibraryName() {
         return libraryName;
     }
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 542bbaf..2b0fde7 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
@@ -40,15 +40,18 @@
     private final String body;
     private final String language;
     private final String kind;
-    private final String library;
+    private final DataverseName libraryDataverseName;
+    private final String libraryName;
+    private final List<String> externalIdentifier;
     private final Boolean deterministic; // null for SQL++ and AQL functions
     private final Boolean nullCall; // null for SQL++ and AQL functions
     private final Map<String, String> resources;
     private final List<List<Triple<DataverseName, String, String>>> dependencies;
 
     public Function(FunctionSignature signature, List<String> paramNames, List<TypeSignature> paramTypes,
-            TypeSignature returnType, String functionBody, String functionKind, String language, String library,
-            Boolean nullCall, Boolean deterministic, Map<String, String> resources,
+            TypeSignature returnType, String functionBody, String functionKind, String language,
+            DataverseName libraryDataverseName, String libraryName, List<String> externalIdentifier, Boolean nullCall,
+            Boolean deterministic, Map<String, String> resources,
             List<List<Triple<DataverseName, String, String>>> dependencies) {
         this.signature = signature;
         this.paramNames = paramNames;
@@ -57,7 +60,9 @@
         this.returnType = returnType;
         this.language = language;
         this.kind = functionKind;
-        this.library = library;
+        this.libraryDataverseName = libraryDataverseName;
+        this.libraryName = libraryName;
+        this.externalIdentifier = externalIdentifier;
         this.nullCall = nullCall;
         this.deterministic = deterministic;
         this.resources = resources == null ? Collections.emptyMap() : resources;
@@ -107,11 +112,19 @@
     }
 
     public boolean isExternal() {
-        return library != null;
+        return externalIdentifier != null;
     }
 
-    public String getLibrary() {
-        return library;
+    public DataverseName getLibraryDataverseName() {
+        return libraryDataverseName;
+    }
+
+    public String getLibraryName() {
+        return libraryName;
+    }
+
+    public List<String> getExternalIdentifier() {
+        return externalIdentifier;
     }
 
     public Boolean getNullCall() {
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasourceAdapterTupleTranslator.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasourceAdapterTupleTranslator.java
index 6279451..5327b22 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasourceAdapterTupleTranslator.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasourceAdapterTupleTranslator.java
@@ -56,12 +56,21 @@
                 ((AString) adapterRecord.getValueByPos(MetadataRecordTypes.DATASOURCE_ADAPTER_ARECORD_TYPE_FIELD_INDEX))
                         .getStringValue());
 
+        DataverseName libraryDataverseName = null;
+        String libraryName = null;
         int libraryNameIdx = adapterRecord.getType().getFieldIndex(MetadataRecordTypes.FIELD_NAME_LIBRARY_NAME);
-        String libraryName =
-                libraryNameIdx >= 0 ? ((AString) adapterRecord.getValueByPos(libraryNameIdx)).getStringValue() : null;
+        if (libraryNameIdx >= 0) {
+            libraryName = ((AString) adapterRecord.getValueByPos(libraryNameIdx)).getStringValue();
+            int libraryDataverseNameIdx =
+                    adapterRecord.getType().getFieldIndex(MetadataRecordTypes.FIELD_NAME_LIBRARY_DATAVERSE_NAME);
+            libraryDataverseName = libraryDataverseNameIdx >= 0
+                    ? DataverseName.createFromCanonicalForm(
+                            ((AString) adapterRecord.getValueByPos(libraryDataverseNameIdx)).getStringValue())
+                    : dataverseName;
+        }
 
-        return new DatasourceAdapter(new AdapterIdentifier(dataverseName, adapterName), classname, adapterType,
-                libraryName);
+        return new DatasourceAdapter(new AdapterIdentifier(dataverseName, adapterName), adapterType, classname,
+                libraryDataverseName, libraryName);
     }
 
     @Override
@@ -132,6 +141,15 @@
         if (adapter.getLibraryName() == null) {
             return;
         }
+
+        fieldName.reset();
+        aString.setValue(MetadataRecordTypes.FIELD_NAME_LIBRARY_DATAVERSE_NAME);
+        stringSerde.serialize(aString, fieldName.getDataOutput());
+        fieldValue.reset();
+        aString.setValue(adapter.getLibraryDataverseName().getCanonicalForm());
+        stringSerde.serialize(aString, fieldValue.getDataOutput());
+        recordBuilder.addField(fieldName, fieldValue);
+
         fieldName.reset();
         aString.setValue(MetadataRecordTypes.FIELD_NAME_LIBRARY_NAME);
         stringSerde.serialize(aString, fieldName.getDataOutput());
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 7135305..9c8d678 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
@@ -20,20 +20,25 @@
 package org.apache.asterix.metadata.entitytupletranslators;
 
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_DATAVERSE_NAME;
+import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_LIBRARY_DATAVERSE_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_RETURN_TYPE_DATAVERSE_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_TYPE;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_VALUE;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_DETERMINISTIC_FIELD_NAME;
+import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_EXTERNAL_IDENTIFIER_FIELD_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_LIBRARY_FIELD_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_NULLCALL_FIELD_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_PARAMTYPES_FIELD_NAME;
-import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_WITHPARAM_LIST_NAME;
+import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_RESOURCES_FIELD_NAME;
+import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_WITHPARAMS_FIELD_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.PROPERTIES_NAME_FIELD_NAME;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.PROPERTIES_VALUE_FIELD_NAME;
 
 import java.io.DataOutput;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -41,6 +46,9 @@
 import org.apache.asterix.builders.IARecordBuilder;
 import org.apache.asterix.builders.OrderedListBuilder;
 import org.apache.asterix.builders.RecordBuilder;
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.functions.ExternalFunctionLanguage;
 import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.common.transactions.TxnId;
@@ -60,6 +68,7 @@
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.TypeSignature;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.common.utils.Triple;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
@@ -93,7 +102,7 @@
         }
     }
 
-    protected Function createMetadataEntityFromARecord(ARecord functionRecord) {
+    protected Function createMetadataEntityFromARecord(ARecord functionRecord) throws AlgebricksException {
         String dataverseCanonicalName =
                 ((AString) functionRecord.getValueByPos(MetadataRecordTypes.FUNCTION_ARECORD_DATAVERSENAME_FIELD_INDEX))
                         .getStringValue();
@@ -125,11 +134,41 @@
         String functionKind =
                 ((AString) functionRecord.getValueByPos(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_KIND_FIELD_INDEX))
                         .getStringValue();
-        String functionLibrary = getString(functionRecord, FUNCTION_ARECORD_FUNCTION_LIBRARY_FIELD_NAME);
-        Boolean nullCall = getBoolean(functionRecord, FUNCTION_ARECORD_FUNCTION_NULLCALL_FIELD_NAME);
-        Boolean deterministic = getBoolean(functionRecord, FUNCTION_ARECORD_FUNCTION_DETERMINISTIC_FIELD_NAME);
 
-        Map<String, String> resources = getResources(functionRecord);
+        Map<String, String> resources = null;
+        DataverseName libraryDataverseName = null;
+        String libraryName;
+        List<String> externalIdentifier = null;
+        AOrderedList externalIdentifierList =
+                getOrderedList(functionRecord, FUNCTION_ARECORD_FUNCTION_EXTERNAL_IDENTIFIER_FIELD_NAME);
+        if (externalIdentifierList != null) {
+            externalIdentifier = new ArrayList<>(externalIdentifierList.size());
+            IACursor externalIdentifierCursor = externalIdentifierList.getCursor();
+            while (externalIdentifierCursor.next()) {
+                externalIdentifierList.add(externalIdentifierCursor.get());
+            }
+            libraryName = getString(functionRecord, MetadataRecordTypes.FIELD_NAME_LIBRARY_NAME);
+            String libraryDataverseCanonicalName = getString(functionRecord, FIELD_NAME_LIBRARY_DATAVERSE_NAME);
+            libraryDataverseName = DataverseName.createFromCanonicalForm(libraryDataverseCanonicalName);
+            resources = getResources(functionRecord, FUNCTION_ARECORD_FUNCTION_RESOURCES_FIELD_NAME);
+            definition = null;
+        } else {
+            // back-compat. get external identifier from function body
+            libraryName = getString(functionRecord, FUNCTION_ARECORD_FUNCTION_LIBRARY_FIELD_NAME);
+            if (libraryName != null) {
+                libraryDataverseName = dataverseName;
+                externalIdentifier =
+                        decodeExternalIdentifierBackCompat(definition, ExternalFunctionLanguage.valueOf(language));
+                resources = getResources(functionRecord, FUNCTION_ARECORD_FUNCTION_WITHPARAMS_FIELD_NAME);
+            }
+        }
+
+        Boolean nullCall = null;
+        Boolean deterministic = null;
+        if (externalIdentifier != null) {
+            nullCall = getBoolean(functionRecord, FUNCTION_ARECORD_FUNCTION_NULLCALL_FIELD_NAME);
+            deterministic = getBoolean(functionRecord, FUNCTION_ARECORD_FUNCTION_DETERMINISTIC_FIELD_NAME);
+        }
 
         IACursor dependenciesCursor = ((AOrderedList) functionRecord
                 .getValueByPos(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_DEPENDENCIES_FIELD_INDEX)).getCursor();
@@ -148,7 +187,8 @@
         FunctionSignature signature = new FunctionSignature(dataverseName, functionName, arity);
 
         return new Function(signature, paramNames, paramTypes, returnType, definition, functionKind, language,
-                functionLibrary, nullCall, deterministic, resources, dependencies);
+                libraryDataverseName, libraryName, externalIdentifier, nullCall, deterministic, resources,
+                dependencies);
     }
 
     private List<TypeSignature> getParamTypes(ARecord functionRecord, int arity, DataverseName functionDataverseName) {
@@ -201,11 +241,12 @@
         return new Triple<>(dataverseName, second, third);
     }
 
-    private Map<String, String> getResources(ARecord functionRecord) {
-        Map<String, String> adaptorConfiguration = new HashMap<>();
+    private Map<String, String> getResources(ARecord functionRecord, String resourcesFieldName) {
+        Map<String, String> adaptorConfiguration = null;
         final ARecordType functionType = functionRecord.getType();
-        final int functionLibraryIdx = functionType.getFieldIndex(FUNCTION_ARECORD_FUNCTION_WITHPARAM_LIST_NAME);
+        final int functionLibraryIdx = functionType.getFieldIndex(resourcesFieldName);
         if (functionLibraryIdx >= 0) {
+            adaptorConfiguration = new HashMap<>();
             IACursor cursor = ((AOrderedList) functionRecord.getValueByPos(functionLibraryIdx)).getCursor();
             while (cursor.next()) {
                 ARecord field = (ARecord) cursor.get();
@@ -222,8 +263,8 @@
 
     private String getString(ARecord aRecord, String fieldName) {
         final ARecordType functionType = aRecord.getType();
-        final int functionLibraryIdx = functionType.getFieldIndex(fieldName);
-        return functionLibraryIdx >= 0 ? ((AString) aRecord.getValueByPos(functionLibraryIdx)).getStringValue() : null;
+        final int fieldIndex = functionType.getFieldIndex(fieldName);
+        return fieldIndex >= 0 ? ((AString) aRecord.getValueByPos(fieldIndex)).getStringValue() : null;
     }
 
     private Boolean getBoolean(ARecord aRecord, String fieldName) {
@@ -232,6 +273,12 @@
         return fieldIndex >= 0 ? ((ABoolean) aRecord.getValueByPos(fieldIndex)).getBoolean() : null;
     }
 
+    private AOrderedList getOrderedList(ARecord aRecord, String fieldName) {
+        final ARecordType aRecordType = aRecord.getType();
+        final int fieldIndex = aRecordType.getFieldIndex(fieldName);
+        return fieldIndex >= 0 ? ((AOrderedList) aRecord.getValueByPos(fieldIndex)) : null;
+    }
+
     @Override
     public ITupleReference getTupleFromMetadataEntity(Function function) throws HyracksDataException {
         DataverseName dataverseName = function.getDataverseName();
@@ -295,7 +342,7 @@
 
         // write field 5
         fieldValue.reset();
-        aString.setValue(function.getFunctionBody());
+        aString.setValue(function.isExternal() ? "" : function.getFunctionBody());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
         recordBuilder.addField(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_DEFINITION_FIELD_INDEX, fieldValue);
 
@@ -375,7 +422,7 @@
         listBuilder.write(fieldValue.getDataOutput(), true);
 
         fieldName.reset();
-        aString.setValue(FUNCTION_ARECORD_FUNCTION_WITHPARAM_LIST_NAME);
+        aString.setValue(FUNCTION_ARECORD_FUNCTION_RESOURCES_FIELD_NAME);
         stringSerde.serialize(aString, fieldName.getDataOutput());
 
         recordBuilder.addField(fieldName, fieldValue);
@@ -402,16 +449,41 @@
     }
 
     protected void writeLibrary(Function function) throws HyracksDataException {
-        if (function.getLibrary() == null) {
+        if (!function.isExternal()) {
             return;
         }
+
         fieldName.reset();
-        aString.setValue(FUNCTION_ARECORD_FUNCTION_LIBRARY_FIELD_NAME);
+        aString.setValue(FIELD_NAME_LIBRARY_DATAVERSE_NAME);
         stringSerde.serialize(aString, fieldName.getDataOutput());
         fieldValue.reset();
-        aString.setValue(function.getLibrary());
+        aString.setValue(function.getLibraryDataverseName().getCanonicalForm());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
         recordBuilder.addField(fieldName, fieldValue);
+
+        fieldName.reset();
+        aString.setValue(MetadataRecordTypes.FIELD_NAME_LIBRARY_NAME);
+        stringSerde.serialize(aString, fieldName.getDataOutput());
+        fieldValue.reset();
+        aString.setValue(function.getLibraryName());
+        stringSerde.serialize(aString, fieldValue.getDataOutput());
+        recordBuilder.addField(fieldName, fieldValue);
+
+        fieldName.reset();
+        aString.setValue(FUNCTION_ARECORD_FUNCTION_EXTERNAL_IDENTIFIER_FIELD_NAME);
+        stringSerde.serialize(aString, fieldName.getDataOutput());
+        OrderedListBuilder listBuilder = new OrderedListBuilder();
+        ArrayBackedValueStorage itemValue = new ArrayBackedValueStorage();
+        listBuilder.reset(stringList);
+        for (String externalIdPart : function.getExternalIdentifier()) {
+            itemValue.reset();
+            aString.setValue(externalIdPart);
+            stringSerde.serialize(aString, itemValue.getDataOutput());
+            listBuilder.addItem(itemValue);
+        }
+        fieldValue.reset();
+        listBuilder.write(fieldValue.getDataOutput(), true);
+        recordBuilder.addField(fieldName, fieldValue);
     }
 
     protected void writeReturnTypeDataverseName(Function function) throws HyracksDataException {
@@ -520,4 +592,38 @@
         }
         return dependencySubnames;
     }
-}
\ No newline at end of file
+
+    // back-compat
+    private static List<String> decodeExternalIdentifierBackCompat(String encodedValue,
+            ExternalFunctionLanguage language) throws AlgebricksException {
+        switch (language) {
+            case JAVA:
+                // input: class
+                //
+                // output:
+                // [0] = class
+                return Collections.singletonList(encodedValue);
+
+            case PYTHON:
+                // input:
+                //  case 1 (method): package.module:class.method
+                //  case 2 (function): package.module:function
+                //
+                // output:
+                //  case 1:
+                //    [0] = package.module
+                //    [1] = class.method
+                //  case 2:
+                //    [0] = package.module
+                //    [1] = function
+                int idx = encodedValue.lastIndexOf(':');
+                if (idx < 0) {
+                    throw new AsterixException(ErrorCode.METADATA_ERROR, encodedValue);
+                }
+                return Arrays.asList(encodedValue.substring(0, idx), encodedValue.substring(idx + 1));
+
+            default:
+                throw new AsterixException(ErrorCode.METADATA_ERROR, language);
+        }
+    }
+}
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/feeds/FeedMetadataUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/feeds/FeedMetadataUtil.java
index 466778c..d5e941f 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/feeds/FeedMetadataUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/feeds/FeedMetadataUtil.java
@@ -170,8 +170,7 @@
     private static ITypedAdapterFactory createExternalAdapterFactory(MetadataTransactionContext mdTxnCtx,
             DatasourceAdapter adapterEntity, String adapterFactoryClassname)
             throws AlgebricksException, RemoteException, HyracksDataException {
-        //TODO:library dataverse must be explicitly specified in the adapter entity
-        DataverseName libraryDataverse = adapterEntity.getAdapterIdentifier().getDataverseName();
+        DataverseName libraryDataverse = adapterEntity.getLibraryDataverseName();
         String libraryName = adapterEntity.getLibraryName();
         Library library = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, libraryDataverse, libraryName);
         if (library == null) {
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 6c88621..f37823c 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,14 +19,12 @@
 package org.apache.asterix.metadata.functions;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.functions.ExternalFunctionLanguage;
-import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.metadata.declared.MetadataProvider;
 import org.apache.asterix.metadata.entities.BuiltinTypeMap;
 import org.apache.asterix.metadata.entities.Function;
@@ -37,6 +35,7 @@
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression.FunctionKind;
 import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
+import org.apache.hyracks.api.exceptions.SourceLocation;
 
 public class ExternalFunctionCompilerUtil {
 
@@ -77,17 +76,11 @@
 
         IResultTypeComputer typeComputer = new ExternalTypeComputer(returnType, paramTypes);
 
-        ExternalFunctionLanguage lang;
-        try {
-            lang = ExternalFunctionLanguage.valueOf(function.getLanguage());
-        } catch (IllegalArgumentException e) {
-            throw new AsterixException(ErrorCode.METADATA_ERROR, function.getLanguage());
-        }
-        List<String> externalIdentifier = decodeExternalIdentifier(lang, function.getFunctionBody());
+        ExternalFunctionLanguage lang = getExternalFunctionLanguage(function.getLanguage());
 
-        return new ExternalScalarFunctionInfo(function.getSignature().createFunctionIdentifier(), returnType,
-                externalIdentifier, lang, function.getLibrary(), paramTypes, function.getResources(),
-                function.getDeterministic(), typeComputer);
+        return new ExternalScalarFunctionInfo(function.getSignature().createFunctionIdentifier(), paramTypes,
+                returnType, typeComputer, lang, function.getLibraryDataverseName(), function.getLibraryName(),
+                function.getExternalIdentifier(), function.getResources(), function.getDeterministic());
     }
 
     private static IFunctionInfo getUnnestFunctionInfo(MetadataProvider metadataProvider, Function function) {
@@ -115,79 +108,31 @@
         return type;
     }
 
-    public static String encodeExternalIdentifier(FunctionSignature functionSignature,
-            ExternalFunctionLanguage language, List<String> identList) throws AlgebricksException {
-        switch (language) {
-            case JAVA:
-                // input:
-                // [0] = package.class
-                //
-                // output: package.class
-
-                return identList.get(0);
-
-            case PYTHON:
-                // input: either a method or a top-level function
-                // [0] = package.module(:class)?
-                // [1] = (function_or_method)? - if missing then defaults to declared function name
-                //
-                // output:
-                // case 1 (method): package.module:class.method
-                // case 2 (function): package.module:function
-
-                String ident0 = identList.get(0);
-                String ident1 = identList.size() > 1 ? identList.get(1) : functionSignature.getName();
-                boolean classExists = ident0.indexOf(':') > 0;
-                return ident0 + (classExists ? '.' : ':') + ident1;
-
-            default:
-                throw new AsterixException(ErrorCode.COMPILATION_ERROR, language);
+    public static ExternalFunctionLanguage getExternalFunctionLanguage(String language) throws AsterixException {
+        try {
+            return ExternalFunctionLanguage.valueOf(language);
+        } catch (IllegalArgumentException e) {
+            throw new AsterixException(ErrorCode.METADATA_ERROR, language);
         }
     }
 
-    public static List<String> decodeExternalIdentifier(ExternalFunctionLanguage language, String encodedValue)
-            throws AlgebricksException {
+    public static void validateExternalIdentifier(List<String> externalIdentifier, ExternalFunctionLanguage language,
+            SourceLocation sourceLoc) throws CompilationException {
+        int expectedSize;
         switch (language) {
             case JAVA:
-                // input: class
-                //
-                // output:
-                // [0] = class
-                return Collections.singletonList(encodedValue);
-
+                expectedSize = 1;
+                break;
             case PYTHON:
-                // input:
-                //  case 1 (method): package.module:class.method
-                //  case 2 (function): package.module:function
-                //
-                // output:
-                //  case 1:
-                //    [0] = package.module
-                //    [1] = class
-                //    [2] = method
-                //  case 2:
-                //    [0] = package.module
-                //    [1] = function
-
-                int d1 = encodedValue.indexOf(':');
-                if (d1 <= 0) {
-                    throw new AsterixException(ErrorCode.COMPILATION_ERROR, encodedValue);
-                }
-                String moduleName = encodedValue.substring(0, d1);
-                int d2 = encodedValue.lastIndexOf('.');
-                if (d2 > d1) {
-                    // class.method
-                    String className = encodedValue.substring(d1 + 1, d2);
-                    String methodName = encodedValue.substring(d2 + 1);
-                    return Arrays.asList(moduleName, className, methodName);
-                } else {
-                    // function
-                    String functionName = encodedValue.substring(d1 + 1);
-                    return Arrays.asList(moduleName, functionName);
-                }
-
+                expectedSize = 2;
+                break;
             default:
-                throw new AsterixException(ErrorCode.COMPILATION_ERROR, language);
+                throw new CompilationException(ErrorCode.METADATA_ERROR, language.name());
+        }
+        int actualSize = externalIdentifier.size();
+        if (actualSize != expectedSize) {
+            throw new CompilationException(ErrorCode.INVALID_EXTERNAL_IDENTIFIER_SIZE, sourceLoc,
+                    String.valueOf(actualSize), language.name());
         }
     }
 }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalScalarFunctionInfo.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalScalarFunctionInfo.java
index da28d18..854320a 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalScalarFunctionInfo.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/functions/ExternalScalarFunctionInfo.java
@@ -22,6 +22,7 @@
 import java.util.Map;
 
 import org.apache.asterix.common.functions.ExternalFunctionLanguage;
+import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.om.functions.ExternalFunctionInfo;
 import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
 import org.apache.asterix.om.types.IAType;
@@ -30,19 +31,12 @@
 
 public class ExternalScalarFunctionInfo extends ExternalFunctionInfo {
 
-    private static final long serialVersionUID = 2L;
+    private static final long serialVersionUID = 3L;
 
-    public ExternalScalarFunctionInfo(String namespace, String library, String name, int arity, IAType returnType,
-            List<String> externalIdentifier, ExternalFunctionLanguage language, List<IAType> argumentTypes,
-            Map<String, String> params, boolean deterministic, IResultTypeComputer rtc) {
-        super(namespace, name, arity, FunctionKind.SCALAR, argumentTypes, returnType, rtc, language, library,
-                externalIdentifier, params, deterministic);
-    }
-
-    public ExternalScalarFunctionInfo(FunctionIdentifier fid, IAType returnType, List<String> externalIdentifier,
-            ExternalFunctionLanguage language, String library, List<IAType> argumentTypes, Map<String, String> params,
-            boolean deterministic, IResultTypeComputer rtc) {
-        super(fid, FunctionKind.SCALAR, argumentTypes, returnType, rtc, language, library, externalIdentifier, params,
-                deterministic);
+    public ExternalScalarFunctionInfo(FunctionIdentifier fid, List<IAType> parameterTypes, IAType returnType,
+            IResultTypeComputer rtc, ExternalFunctionLanguage language, DataverseName libraryDataverseName,
+            String libraryName, List<String> externalIdentifier, Map<String, String> resources, boolean deterministic) {
+        super(fid, FunctionKind.SCALAR, parameterTypes, returnType, rtc, language, libraryDataverseName, libraryName,
+                externalIdentifier, resources, deterministic);
     }
 }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/MetadataLockUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/MetadataLockUtil.java
index eab69e0..4309c2e 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/MetadataLockUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/MetadataLockUtil.java
@@ -138,11 +138,14 @@
 
     @Override
     public void createFunctionBegin(IMetadataLockManager lockMgr, LockList locks, DataverseName dataverseName,
-            String functionName, String libraryName) throws AlgebricksException {
+            String functionName, DataverseName libraryDataverseName, String libraryName) throws AlgebricksException {
         lockMgr.acquireDataverseReadLock(locks, dataverseName);
         lockMgr.acquireFunctionWriteLock(locks, dataverseName, functionName);
         if (libraryName != null) {
-            lockMgr.acquireLibraryReadLock(locks, dataverseName, libraryName);
+            if (!dataverseName.equals(libraryDataverseName)) {
+                lockMgr.acquireDataverseReadLock(locks, libraryDataverseName);
+            }
+            lockMgr.acquireLibraryReadLock(locks, libraryDataverseName, libraryName);
         }
     }
 
@@ -155,11 +158,14 @@
 
     @Override
     public void createAdapterBegin(IMetadataLockManager lockMgr, LockList locks, DataverseName dataverseName,
-            String adapterName, String libraryName) throws AlgebricksException {
+            String adapterName, DataverseName libraryDataverseName, String libraryName) throws AlgebricksException {
         lockMgr.acquireDataverseReadLock(locks, dataverseName);
         lockMgr.acquireAdapterWriteLock(locks, dataverseName, adapterName);
         if (libraryName != null) {
-            lockMgr.acquireLibraryReadLock(locks, dataverseName, libraryName);
+            if (!dataverseName.equals(libraryDataverseName)) {
+                lockMgr.acquireDataverseReadLock(locks, libraryDataverseName);
+            }
+            lockMgr.acquireLibraryReadLock(locks, libraryDataverseName, libraryName);
         }
     }
 
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 bdaa3fe..8707018 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
@@ -22,6 +22,7 @@
 import java.util.Map;
 
 import org.apache.asterix.common.functions.ExternalFunctionLanguage;
+import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
 import org.apache.asterix.om.types.IAType;
 import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression.FunctionKind;
@@ -29,54 +30,32 @@
 
 public class ExternalFunctionInfo extends FunctionInfo implements IExternalFunctionInfo {
 
-    private static final long serialVersionUID = 2L;
+    private static final long serialVersionUID = 3L;
 
-    private final transient IResultTypeComputer rtc;
-    private final List<IAType> argumentTypes;
-    private final ExternalFunctionLanguage language;
     private final FunctionKind kind;
+    private final List<IAType> parameterTypes;
     private final IAType returnType;
-    private final String library;
+    private final transient IResultTypeComputer rtc;
+    private final ExternalFunctionLanguage language;
+    private final DataverseName libraryDataverseName;
+    private final String libraryName;
     private final List<String> externalIdentifier;
-    private final Map<String, String> params;
+    private final Map<String, String> resources;
 
-    public ExternalFunctionInfo(String namespace, String name, int arity, FunctionKind kind, List<IAType> argumentTypes,
-            IAType returnType, IResultTypeComputer rtc, ExternalFunctionLanguage language, String library,
-            List<String> externalIdentifier, Map<String, String> params, boolean deterministic) {
-        this(new FunctionIdentifier(namespace, name, arity), kind, argumentTypes, returnType, rtc, language, library,
-                externalIdentifier, params, deterministic);
-    }
-
-    public ExternalFunctionInfo(FunctionIdentifier fid, FunctionKind kind, List<IAType> argumentTypes,
-            IAType returnType, IResultTypeComputer rtc, ExternalFunctionLanguage language, String library,
-            List<String> externalIdentifier, Map<String, String> params, boolean deterministic) {
+    public ExternalFunctionInfo(FunctionIdentifier fid, FunctionKind kind, List<IAType> parameterTypes,
+            IAType returnType, IResultTypeComputer rtc, ExternalFunctionLanguage language,
+            DataverseName libraryDataverseName, String libraryName, List<String> externalIdentifier,
+            Map<String, String> resources, boolean deterministic) {
         super(fid, deterministic);
-        this.rtc = rtc;
-        this.argumentTypes = argumentTypes;
-        this.library = library;
-        this.language = language;
-        this.externalIdentifier = externalIdentifier;
         this.kind = kind;
+        this.parameterTypes = parameterTypes;
         this.returnType = returnType;
-        this.params = params;
-    }
-
-    public IResultTypeComputer getResultTypeComputer() {
-        return rtc;
-    }
-
-    public List<IAType> getArgumentTypes() {
-        return argumentTypes;
-    }
-
-    @Override
-    public List<IAType> getArgumentList() {
-        return argumentTypes;
-    }
-
-    @Override
-    public ExternalFunctionLanguage getLanguage() {
-        return language;
+        this.rtc = rtc;
+        this.language = language;
+        this.libraryDataverseName = libraryDataverseName;
+        this.libraryName = libraryName;
+        this.externalIdentifier = externalIdentifier;
+        this.resources = resources;
     }
 
     @Override
@@ -85,13 +64,31 @@
     }
 
     @Override
+    public List<IAType> getParameterTypes() {
+        return parameterTypes;
+    }
+
+    @Override
     public IAType getReturnType() {
         return returnType;
     }
 
+    public IResultTypeComputer getResultTypeComputer() {
+        return rtc;
+    }
+
     @Override
-    public String getLibrary() {
-        return library;
+    public ExternalFunctionLanguage getLanguage() {
+        return language;
+    }
+
+    public DataverseName getLibraryDataverseName() {
+        return libraryDataverseName;
+    }
+
+    @Override
+    public String getLibraryName() {
+        return libraryName;
     }
 
     @Override
@@ -100,7 +97,7 @@
     }
 
     @Override
-    public Map<String, String> getParams() {
-        return params;
+    public Map<String, String> getResources() {
+        return resources;
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IExternalFunctionInfo.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IExternalFunctionInfo.java
index 1db0c19..d87d6df 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IExternalFunctionInfo.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/IExternalFunctionInfo.java
@@ -22,6 +22,7 @@
 import java.util.Map;
 
 import org.apache.asterix.common.functions.ExternalFunctionLanguage;
+import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
 import org.apache.asterix.om.types.IAType;
 import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression.FunctionKind;
@@ -29,20 +30,21 @@
 
 public interface IExternalFunctionInfo extends IFunctionInfo {
 
-    IResultTypeComputer getResultTypeComputer();
+    FunctionKind getKind();
+
+    List<IAType> getParameterTypes();
 
     IAType getReturnType();
 
-    List<String> getExternalIdentifier();
-
-    List<IAType> getArgumentList();
+    IResultTypeComputer getResultTypeComputer();
 
     ExternalFunctionLanguage getLanguage();
 
-    FunctionKind getKind();
+    DataverseName getLibraryDataverseName();
 
-    String getLibrary();
+    String getLibraryName();
 
-    Map<String, String> getParams();
+    List<String> getExternalIdentifier();
 
+    Map<String, String> getResources();
 }