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

[asterixdb] branch master updated: [NO ISSUE][COMP] UDF improvements

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 20905cd  [NO ISSUE][COMP] UDF improvements
20905cd is described below

commit 20905cd14261d134ada457ddb0e9fcf410b49a58
Author: Dmitry Lychagin <dm...@couchbase.com>
AuthorDate: Thu Aug 6 20:34:55 2020 -0700

    [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 <je...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Dmitry Lychagin <dm...@couchbase.com>
    Reviewed-by: Ian Maxon <im...@uci.edu>
---
 ...duceDynamicTypeCastForExternalFunctionRule.java |   4 +-
 .../asterix/api/http/server/UdfApiServlet.java     |   2 +-
 .../asterix/app/translator/QueryTranslator.java    | 294 ++++++++++++++-------
 .../deterministic/deterministic.2.ddl.sqlpp        |   8 +-
 .../getCapital/getCapital.2.ddl.sqlpp              |   3 +-
 .../getCapital_open/getCapital_open.2.ddl.sqlpp    |   3 +-
 .../keyword_detector/keyword_detector.2.ddl.sqlpp  |   9 +-
 .../my_array_sum/my_array_sum.0.ddl.sqlpp          |   3 +
 .../my_array_sum/my_array_sum.1.lib.sqlpp          |   2 +-
 .../my_array_sum/my_array_sum.2.ddl.sqlpp          |   9 +-
 .../my_array_sum/my_array_sum.4.ddl.sqlpp          |   1 +
 .../mysentiment/mysentiment.2.ddl.sqlpp            |   3 +-
 .../external-library/mysum/mysum.2.ddl.sqlpp       |   9 +-
 .../mysum_dropinuse/mysum_dropinuse.2.ddl.sqlpp    |   3 +-
 .../py_function_error.2.ddl.sqlpp                  |   3 +-
 .../py_nested_access/py_nested_access.2.ddl.sqlpp  |   3 +-
 .../python-fn-escape/python-fn-escape.2.ddl.sqlpp  |   3 +-
 .../return_invalid_type.2.ddl.sqlpp                |   3 +-
 .../type_validation/type_validation.2.ddl.sqlpp    |   3 +-
 .../udf_metadata/udf_metadata.2.ddl.sqlpp          |  18 +-
 .../upperCase/upperCase.2.ddl.sqlpp                |   3 +-
 ...eed-with-external-adapter-cross-dv.0.ddl.sqlpp} |  10 +-
 ...eed-with-external-adapter-cross-dv.1.lib.sqlpp} |   8 +-
 ...eed-with-external-adapter-cross-dv.2.ddl.sqlpp} |  17 +-
 ...-with-external-adapter-cross-dv.3.update.sqlpp} |   9 +-
 ...d-with-external-adapter-cross-dv.4.query.sqlpp} |   9 +-
 ...-with-external-adapter-cross-dv.5.update.sqlpp} |  10 +-
 .../feed-with-external-adapter.2.ddl.sqlpp         |  11 +-
 .../feed-with-external-adapter.5.update.sqlpp}     |   8 +-
 .../feed-with-external-function.2.ddl.sqlpp        |   4 +-
 .../udf_metadata/udf_metadata.3.adm                |  18 +-
 .../feed-with-external-adapter-cross-dv.4.adm      |   4 +
 .../resources/runtimets/testsuite_it_sqlpp.xml     |  10 +-
 .../asterix/common/exceptions/ErrorCode.java       |   5 +
 .../asterix/common/metadata/IMetadataLockUtil.java |   4 +-
 .../src/main/resources/asx_errormsg/en.properties  |   5 +
 .../src/main/user-defined_function/udf.md          |  46 ++--
 .../ExternalScalarJavaFunctionEvaluator.java       |  14 +-
 .../ExternalScalarPythonFunctionEvaluator.java     |  59 +++--
 .../external/library/JavaFunctionHelper.java       |   6 +-
 .../common/statement/AdapterDropStatement.java     |  23 +-
 .../common/statement/CreateAdapterStatement.java   |  58 ++--
 .../common/statement/CreateFunctionStatement.java  |  92 +++++--
 .../common/statement/LibraryDropStatement.java     |   9 +-
 .../lang/common/visitor/FormatPrintVisitor.java    |   8 +-
 .../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj    | 208 +++++++--------
 .../org/apache/asterix/metadata/MetadataNode.java  |   5 +-
 .../metadata/MetadataTransactionContext.java       |   5 +-
 .../metadata/bootstrap/MetadataBootstrap.java      |   2 +-
 .../metadata/bootstrap/MetadataRecordTypes.java    |   9 +-
 .../metadata/entities/DatasourceAdapter.java       |  21 +-
 .../apache/asterix/metadata/entities/Function.java |  27 +-
 .../DatasourceAdapterTupleTranslator.java          |  26 +-
 .../FunctionTupleTranslator.java                   | 142 ++++++++--
 .../asterix/metadata/feeds/FeedMetadataUtil.java   |   3 +-
 .../functions/ExternalFunctionCompilerUtil.java    | 103 ++------
 .../functions/ExternalScalarFunctionInfo.java      |  20 +-
 .../asterix/metadata/utils/MetadataLockUtil.java   |  14 +-
 .../asterix/om/functions/ExternalFunctionInfo.java |  75 +++---
 .../om/functions/IExternalFunctionInfo.java        |  16 +-
 60 files changed, 932 insertions(+), 580 deletions(-)

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 @@ public class IntroduceDynamicTypeCastForExternalFunctionRule implements IAlgebra
         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 @@ public class UdfApiServlet extends BasicAuthServlet {
         }
         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.HashSet;
 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.base.Statement;
 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 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                     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 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
             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 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
     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 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
             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 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 }
             }
 
-            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, "");
-                }
-                ExternalFunctionLanguage functionLang;
-                try {
-                    functionLang = ExternalFunctionLanguage.valueOf(lang.toUpperCase(Locale.ROOT));
-                } catch (IllegalArgumentException e) {
-                    throw new CompilationException(ErrorCode.COMPILATION_INCOMPATIBLE_FUNCTION_LANGUAGE, sourceLoc,
-                            lang);
-                }
-                Library libraryInMetadata = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, dataverseName, libraryName);
-                if (libraryInMetadata == null) {
+                TypeExpression returnTypeExpr = cfs.getReturnType();
+                Pair<TypeSignature, TypeSignature> returnType = translateFunctionParameterType(signature, -1,
+                        returnTypeExpr, isExternal, sourceLoc, metadataProvider, mdTxnCtx);
+                if (returnType.second != null) {
+                    dependentTypes.add(returnType.second);
+                }
+                DataverseName libraryDataverseName = cfs.getLibraryDataverseName();
+                if (libraryDataverseName == null) {
+                    libraryDataverseName = dataverseName;
+                }
+                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 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 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,75 +2167,164 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
         return new Pair<>(resultType, dependentType);
     }
 
+    protected void handleFunctionDropStatement(MetadataProvider metadataProvider, Statement stmt) throws Exception {
+        FunctionDropStatement stmtDropFunction = (FunctionDropStatement) stmt;
+        FunctionSignature signature = stmtDropFunction.getFunctionSignature();
+        signature.setDataverseName(getActiveDataverseName(signature.getDataverseName()));
+        lockUtil.dropFunctionBegin(lockManager, metadataProvider.getLocks(), signature.getDataverseName(),
+                signature.getName());
+        try {
+            doDropFunction(metadataProvider, stmtDropFunction, signature);
+        } finally {
+            metadataProvider.getLocks().unlock();
+        }
+    }
+
+    protected boolean doDropFunction(MetadataProvider metadataProvider, FunctionDropStatement stmtDropFunction,
+            FunctionSignature signature) throws Exception {
+        SourceLocation sourceLoc = stmtDropFunction.getSourceLocation();
+        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()) {
+                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    return false;
+                } else {
+                    throw new CompilationException(ErrorCode.UNKNOWN_FUNCTION, sourceLoc, signature.toString(false));
+                }
+            }
+
+            MetadataManager.INSTANCE.dropFunction(mdTxnCtx, signature);
+            MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+            return true;
+        } catch (Exception e) {
+            abort(e, e, mdTxnCtx);
+            throw e;
+        }
+    }
+
     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());
+        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);
-        String libraryName = cas.getLibName();
-        lockUtil.createAdapterBegin(lockManager, metadataProvider.getLocks(), dataverse, aid.getName(), libraryName);
         try {
-            Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverse);
+            DataverseName dataverseName = getActiveDataverseName(cas.getDataverseName());
+            Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, dataverseName);
             if (dv == null) {
-                throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverse);
+                throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc, dataverseName);
             }
-            Library libraryInMetadata = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, dataverse, libraryName);
-            if (libraryInMetadata == null) {
+            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
-            String adapterFactoryClass = cas.getExternalIdent();
-            DatasourceAdapter dsa = new DatasourceAdapter(aid, adapterFactoryClass,
-                    IDataSourceAdapter.AdapterType.EXTERNAL, libraryName);
-            MetadataManager.INSTANCE.addAdapter(mdTxnCtx, dsa);
+            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: " + aid.getName());
+                LOGGER.info("Installed adapter: " + adapterName);
             }
             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();
-        signature.setDataverseName(getActiveDataverseName(signature.getDataverseName()));
-        lockUtil.dropFunctionBegin(lockManager, metadataProvider.getLocks(), signature.getDataverseName(),
-                signature.getName());
+    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 {
-            doDropFunction(metadataProvider, stmtDropFunction, signature);
+            doDropAdapter(metadataProvider, stmtDropAdapter, dataverseName, adapterName);
         } finally {
             metadataProvider.getLocks().unlock();
         }
     }
 
-    protected boolean doDropFunction(MetadataProvider metadataProvider, FunctionDropStatement stmtDropFunction,
-            FunctionSignature signature) throws Exception {
-        SourceLocation sourceLoc = stmtDropFunction.getSourceLocation();
+    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 {
-            Function function = MetadataManager.INSTANCE.getFunction(mdTxnCtx, signature);
-            if (function == null) {
-                if (stmtDropFunction.getIfExists()) {
+            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_FUNCTION, sourceLoc, signature.toString(false));
+                    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.dropFunction(mdTxnCtx, signature);
+            MetadataManager.INSTANCE.dropAdapter(mdTxnCtx, dataverseName, adapterName);
             MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
             return true;
         } catch (Exception e) {
@@ -2360,26 +2463,38 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
         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 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
             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
+ */
+
+use externallibtest;
 
-create function my_array_sum(a: [int32]) returns int32 language java as "testlib","org.apache.asterix.external.library.MyArraySumFactory";
+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/external-library/getCapital/getCapital.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.0.ddl.sqlpp
similarity index 75%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
copy to 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
index a03b570..904e960 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/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.0.ddl.sqlpp
@@ -16,7 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+/*
+ * Description  : Feed with an external adapter. Test different dataverses.
+ * Expected Res : Success
+ */
 
-use externallibtest;
+drop dataverse externallibtest if exists;
+create dataverse externallibtest;
 
-create function getCapital(a: string) returns CountryCapitalType language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+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/getCapital/getCapital.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.1.lib.sqlpp
similarity index 81%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
copy to 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
index a03b570..806ad5e 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/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.1.lib.sqlpp
@@ -16,7 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
 
-use externallibtest;
-
-create function getCapital(a: string) returns CountryCapitalType language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+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/feed-with-external-adapter.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
similarity index 62%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.2.ddl.sqlpp
copy to 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
index 9cb4046..46a5fe9 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-cross-dv/feed-with-external-adapter-cross-dv.2.ddl.sqlpp
@@ -21,18 +21,23 @@
  * Expected Res : Success
  */
 
-use externallibtest;
+/* test drop adapter if exists */
+drop adapter externallibtest.TweetAdapter if exists;
 
-create adapter TweetAdapter language java as "testlib",
-  "org.apache.asterix.external.library.adapter.TestTypedAdapterFactory";
+create adapter externallibtest.TweetAdapter
+  as "org.apache.asterix.external.library.adapter.TestTypedAdapterFactory" at externallibtest2.testlib;
 
-create type TweetType as open {
+/* 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 Tweets(TweetType) primary key tweetid;
+create dataset externallibtest.Tweets(TweetType) primary key tweetid;
 
-create feed TweetFeed with {
+create feed externallibtest.TweetFeed with {
   "adapter-name": "TweetAdapter",
   "type-name" : "TweetType",
   "num_output_records": 4
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/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.3.update.sqlpp
similarity index 77%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
copy to 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
index a03b570..ae44476 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/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.3.update.sqlpp
@@ -16,7 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
 
-use externallibtest;
+set `wait-for-completion-feed` "true";
 
-create function getCapital(a: string) returns CountryCapitalType language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+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/external-library/mysentiment/mysentiment.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.4.query.sqlpp
similarity index 84%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/mysentiment/mysentiment.2.ddl.sqlpp
copy to 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
index ff39aee..5eccf60 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/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.4.query.sqlpp
@@ -16,6 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
- USE externallibtest;
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
 
-create function sentiment(s) language python as "testlib","sentiment:TweetSent";
+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/external-library/getCapital/getCapital.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.5.update.sqlpp
similarity index 77%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
copy to 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
index a03b570..62f4960 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/feeds/feed-with-external-adapter-cross-dv/feed-with-external-adapter-cross-dv.5.update.sqlpp
@@ -16,7 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
 
-use externallibtest;
-
-create function getCapital(a: string) returns CountryCapitalType language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+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/external-library/getCapital/getCapital.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.5.update.sqlpp
similarity index 82%
copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/external-library/getCapital/getCapital.2.ddl.sqlpp
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/feeds/feed-with-external-adapter/feed-with-external-adapter.5.update.sqlpp
index a03b570..e897463 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/feeds/feed-with-external-adapter/feed-with-external-adapter.5.update.sqlpp
@@ -16,7 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+/*
+ * Description  : Feed with an external adapter
+ * Expected Res : Success
+ */
 
 use externallibtest;
 
-create function getCapital(a: string) returns CountryCapitalType language java as "testlib","org.apache.asterix.external.library.CapitalFinderFactory";
+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", "Nu [...]
-{ "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", "NullCa [...]
+{ "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" ], "NullCa [...]
+{ "fn": { "DataverseName": "externallibtest", "Name": "myfn004", "Arity": "2", "Params": [ "a", "b" ], "ReturnType": "CountryCapitalType", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "CountryCapitalType" }, { "Type": "$f$t$myfn004$2$1" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIdentifier": [ "org.apache.asterix.external.library.Ca [...]
+{ "fn": { "DataverseName": "externallibtest", "Name": "myfn005", "Arity": "4", "Params": [ "a", "b", "c", "d" ], "ReturnType": "string", "Definition": "", "Language": "JAVA", "Kind": "SCALAR", "Dependencies": [ [  ], [  ], [ [ "externallibtest", "CountryCapitalType" ] ] ], "ParamTypes": [ { "Type": "int16" }, { "Type": "$f$t$myfn005$4$1" }, { "Type": "CountryCapitalType" }, { "Type": "$f$t$myfn005$4$3" } ], "LibraryDataverseName": "externallibtest", "LibraryName": "testlib", "ExternalIde [...]
+{ "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, " [...]
+{ "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", "ExternalIdenti [...]
 { "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 class ErrorCode {
     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 @@ public interface IMetadataLockUtil {
     // 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 @@ public interface IMetadataLockUtil {
     // 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 @@ Any response other than `200` indicates an error in deployment.
 
 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 @@ scikit-learn here (our method is obviously better!), but it's just included as a
 
     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 @@ With the library deployed, we can define a function within it for use. For examp
 
     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 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:
 
-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.
+    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 @@ Please refer to section [Data Ingestion](feeds.html) if you have any trouble in
 
 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 java.io.IOException;
 
 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 @@ class ExternalScalarJavaFunctionEvaluator extends ExternalScalarFunctionEvaluato
             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 @@ class ExternalScalarPythonFunctionEvaluator extends ExternalScalarFunctionEvalua
         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 @@ class ExternalScalarPythonFunctionEvaluator extends ExternalScalarFunctionEvalua
         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 @@ class ExternalScalarPythonFunctionEvaluator extends ExternalScalarFunctionEvalua
 
         @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 @@ class ExternalScalarPythonFunctionEvaluator extends ExternalScalarFunctionEvalua
 
     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 @@ class ExternalScalarPythonFunctionEvaluator extends ExternalScalarFunctionEvalua
             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 @@ public class JavaFunctionHelper implements IFunctionHelper {
         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 @@ public class AdapterDropStatement extends AbstractStatement {
         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 class AdapterDropStatement extends AbstractStatement {
     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;
-        this.ifNotExists = ifNotExists;
+    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 @@ public class CreateAdapterStatement extends AbstractStatement {
         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 class CreateAdapterStatement extends AbstractStatement {
     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.struct.VarIdentifier;
 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 @@ public class CreateFunctionStatement extends AbstractStatement {
     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 @@ public class CreateFunctionStatement extends AbstractStatement {
         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 @@ public class CreateFunctionStatement extends AbstractStatement {
         return externalIdentifier;
     }
 
-    public String getLibName() {
-        return libName;
+    public DataverseName getLibraryDataverseName() {
+        return libraryDataverseName;
+    }
+
+    public String getLibraryName() {
+        return libraryName;
     }
 
-    public String getLang() {
-        return lang;
+    public boolean getNullCall() throws CompilationException {
+        Boolean nullCall = getBooleanOption(NULLCALL_FIELD_NAME);
+        return nullCall != null ? nullCall : NULLCALL_DEFAULT;
     }
 
-    public Boolean getDeterministic() {
-        return deterministic;
+    public boolean getDeterministic() throws CompilationException {
+        Boolean deterministic = getBooleanOption(DETERMINISTIC_FIELD_NAME);
+        return deterministic != null ? deterministic : DETERMINISTIC_DEFAULT;
     }
 
-    public Boolean getNullCall() {
-        return nullCall;
+    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 @@ public class CreateFunctionStatement extends AbstractStatement {
         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 @@ public class CreateFunctionStatement extends AbstractStatement {
         }
         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 @@ public final class LibraryDropStatement extends AbstractStatement {
     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 @@ public final class LibraryDropStatement extends AbstractStatement {
         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.config.DatasetConfig.IndexType;
 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 @@ public abstract class FormatPrintVisitor implements ILangVisitor<Void, Integer>
     @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 @@ public abstract class FormatPrintVisitor implements ILangVisitor<Void, Integer>
     @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.NullLiteral;
 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 @@ class SQLPPParser extends ScopeChecker implements IParser {
     private static final String SETS = "SETS";
     private static final String TIES = "TIES";
     private static final String UNBOUNDED = "UNBOUNDED";
-    private static final String 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,74 +1095,61 @@ CreateDataverseStatement DataverseSpecification(Token startStmtToken) throws Par
   }
 }
 
-CreateFunctionStatement CreateFunctionStatement(Token startStmtToken) throws ParseException:
+CreateAdapterStatement CreateAdapterStatement(Token startStmtToken) throws ParseException:
 {
-  CreateFunctionStatement stmt = null;
+  CreateAdapterStatement stmt = null;
 }
 {
-  <FUNCTION> stmt = FunctionSpecification(startStmtToken)
+  <ADAPTER> stmt = AdapterSpecification(startStmtToken)
   {
     return stmt;
   }
 }
 
-
-CreateAdapterStatement CreateAdapterStatement(Token startStmtToken) throws ParseException:
+CreateAdapterStatement AdapterSpecification(Token startStmtToken) throws ParseException:
 {
-  CreateAdapterStatement stmt = null;
+  Pair<DataverseName,Identifier> adapterName = null;
+  Pair<DataverseName,Identifier> libraryName = null;
+  List<String> externalIdentifier = null;
+  boolean ifNotExists = false;
 }
 {
-  <ADAPTER> stmt = AdapterSpecification(startStmtToken)
+  adapterName = QualifiedName()
+  ifNotExists = IfNotExists()
+  <AS> externalIdentifier = FunctionExternalIdentifier()
+  <AT> libraryName = QualifiedName()
   {
-    return stmt;
+    CreateAdapterStatement stmt = new CreateAdapterStatement(adapterName.first, adapterName.second.getValue(),
+      libraryName.first, libraryName.second.getValue(), externalIdentifier, ifNotExists);
+    return addSourceLocation(stmt, startStmtToken);
   }
 }
 
-CreateAdapterStatement AdapterSpecification(Token startStmtToken) throws ParseException:
+CreateFunctionStatement CreateFunctionStatement(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;
-  boolean ifNotExists = false;
+  CreateFunctionStatement stmt = null;
 }
 {
-  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);
-    }
-
+  <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 @@ CreateFunctionStatement FunctionSpecification(Token startStmtToken) throws Parse
     (
       <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 @@ Expression FunctionBody() throws ParseException:
   }
 }
 
+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 @@ Statement DropStatement() throws ParseException:
     | 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 @@ DataverseDropStatement DropDataverseSpecification(Token startStmtToken) throws P
   }
 }
 
+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 @@ public class MetadataNode implements IMetadataNode {
             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 @@ public class MetadataNode implements IMetadataNode {
         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 class MetadataTransactionContext extends MetadataCache {
     }
 
     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 @@ public class MetadataBootstrap {
             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 final class MetadataRecordTypes {
     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 final class MetadataRecordTypes {
     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 @@ public class DatasourceAdapter implements IMetadataEntity<DatasourceAdapter> {
         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 @@ public class Function implements IMetadataEntity<Function> {
     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 @@ public class Function implements IMetadataEntity<Function> {
         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 class Function implements IMetadataEntity<Function> {
     }
 
     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 @@ public class DatasourceAdapterTupleTranslator extends AbstractTupleTranslator<Da
                 ((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 @@ public class DatasourceAdapterTupleTranslator extends AbstractTupleTranslator<Da
         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 java.util.Map;
 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.AOrderedListType;
 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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
         }
     }
 
-    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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
         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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
         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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
         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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
 
     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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
         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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
 
         // 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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
         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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
     }
 
     protected void writeLibrary(Function function) throws HyracksDataException {
-        if (function.getLibrary() == null) {
+        if (!function.isExternal()) {
             return;
         }
+
+        fieldName.reset();
+        aString.setValue(FIELD_NAME_LIBRARY_DATAVERSE_NAME);
+        stringSerde.serialize(aString, fieldName.getDataOutput());
+        fieldValue.reset();
+        aString.setValue(function.getLibraryDataverseName().getCanonicalForm());
+        stringSerde.serialize(aString, fieldValue.getDataOutput());
+        recordBuilder.addField(fieldName, fieldValue);
+
         fieldName.reset();
-        aString.setValue(FUNCTION_ARECORD_FUNCTION_LIBRARY_FIELD_NAME);
+        aString.setValue(MetadataRecordTypes.FIELD_NAME_LIBRARY_NAME);
         stringSerde.serialize(aString, fieldName.getDataOutput());
         fieldValue.reset();
-        aString.setValue(function.getLibrary());
+        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 @@ public class FunctionTupleTranslator extends AbstractDatatypeTupleTranslator<Fun
         }
         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 @@ public class FeedMetadataUtil {
     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.asterix.om.types.TypeSignature;
 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 @@ public class ExternalFunctionCompilerUtil {
 
         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 @@ public class ExternalFunctionCompilerUtil {
         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.List;
 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 @@ import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 
 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 @@ public class MetadataLockUtil implements IMetadataLockUtil {
 
     @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 @@ public class MetadataLockUtil implements IMetadataLockUtil {
 
     @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.List;
 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,69 +30,65 @@ import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 
 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.kind = kind;
+        this.parameterTypes = parameterTypes;
+        this.returnType = returnType;
         this.rtc = rtc;
-        this.argumentTypes = argumentTypes;
-        this.library = library;
         this.language = language;
+        this.libraryDataverseName = libraryDataverseName;
+        this.libraryName = libraryName;
         this.externalIdentifier = externalIdentifier;
-        this.kind = kind;
-        this.returnType = returnType;
-        this.params = params;
+        this.resources = resources;
     }
 
-    public IResultTypeComputer getResultTypeComputer() {
-        return rtc;
+    @Override
+    public FunctionKind getKind() {
+        return kind;
     }
 
-    public List<IAType> getArgumentTypes() {
-        return argumentTypes;
+    @Override
+    public List<IAType> getParameterTypes() {
+        return parameterTypes;
     }
 
     @Override
-    public List<IAType> getArgumentList() {
-        return argumentTypes;
+    public IAType getReturnType() {
+        return returnType;
     }
 
-    @Override
-    public ExternalFunctionLanguage getLanguage() {
-        return language;
+    public IResultTypeComputer getResultTypeComputer() {
+        return rtc;
     }
 
     @Override
-    public FunctionKind getKind() {
-        return kind;
+    public ExternalFunctionLanguage getLanguage() {
+        return language;
     }
 
-    @Override
-    public IAType getReturnType() {
-        return returnType;
+    public DataverseName getLibraryDataverseName() {
+        return libraryDataverseName;
     }
 
     @Override
-    public String getLibrary() {
-        return library;
+    public String getLibraryName() {
+        return libraryName;
     }
 
     @Override
@@ -100,7 +97,7 @@ public class ExternalFunctionInfo extends FunctionInfo implements IExternalFunct
     }
 
     @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.List;
 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 @@ import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
 
 public interface IExternalFunctionInfo extends IFunctionInfo {
 
-    IResultTypeComputer getResultTypeComputer();
+    FunctionKind getKind();
 
-    IAType getReturnType();
+    List<IAType> getParameterTypes();
 
-    List<String> getExternalIdentifier();
+    IAType getReturnType();
 
-    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();
 }