diff --git a/basex-core/src/main/java/org/basex/query/QueryContext.java b/basex-core/src/main/java/org/basex/query/QueryContext.java
index d4a99ff918..c446ef4f34 100644
--- a/basex-core/src/main/java/org/basex/query/QueryContext.java
+++ b/basex-core/src/main/java/org/basex/query/QueryContext.java
@@ -153,9 +153,10 @@ public final class QueryContext extends Job implements Closeable {
/**
* Constructor.
* @param parent parent context
+ * @param nsContext inherited namespace context (can be {@code null})
*/
- public QueryContext(final QueryContext parent) {
- this(parent.context, parent, parent.info);
+ public QueryContext(final QueryContext parent, final NSDynContext nsContext) {
+ this(parent.context, parent, nsContext, parent.info);
parent.pushJob(this);
updates = parent.updates;
}
@@ -165,20 +166,22 @@ public QueryContext(final QueryContext parent) {
* @param context database context
*/
public QueryContext(final Context context) {
- this(context, null, null);
+ this(context, null, null, null);
}
/**
* Constructor.
* @param context database context
* @param parent parent context (can be {@code null})
+ * @param nsContext inherited namespace context (can be {@code null})
* @param info query info (can be {@code null})
*/
- public QueryContext(final Context context, final QueryContext parent, final QueryInfo info) {
+ public QueryContext(final Context context, final QueryContext parent,
+ final NSDynContext nsContext, final QueryInfo info) {
this.context = context;
this.parent = parent;
this.info = info != null ? info : new QueryInfo(context);
- ns = parent != null ? new NSDynContext(parent.ns) : new NSDynContext(null);
+ ns = new NSDynContext(nsContext);
resources = parent != null ? parent.resources : new QueryResources(this);
ftPosData = parent != null ? parent.ftPosData : null;
shared = parent != null ? parent.shared : new SharedData();
diff --git a/basex-core/src/main/java/org/basex/query/QueryProcessor.java b/basex-core/src/main/java/org/basex/query/QueryProcessor.java
index 5f7229d20f..fde8ff4045 100644
--- a/basex-core/src/main/java/org/basex/query/QueryProcessor.java
+++ b/basex-core/src/main/java/org/basex/query/QueryProcessor.java
@@ -50,7 +50,7 @@ public QueryProcessor(final String query, final Context ctx) {
public QueryProcessor(final String query, final String uri, final Context ctx,
final QueryInfo info) {
this.query = query;
- qc = pushJob(new QueryContext(ctx, null, info));
+ qc = pushJob(new QueryContext(ctx, null, null, info));
sc = new StaticContext(qc);
sc.baseURI(uri != null && uri.isEmpty() ? "./" : uri);
}
diff --git a/basex-core/src/main/java/org/basex/query/func/fn/FnLoadXQueryModule.java b/basex-core/src/main/java/org/basex/query/func/fn/FnLoadXQueryModule.java
index ef35094e1e..83ec4e1ba7 100644
--- a/basex-core/src/main/java/org/basex/query/func/fn/FnLoadXQueryModule.java
+++ b/basex-core/src/main/java/org/basex/query/func/fn/FnLoadXQueryModule.java
@@ -91,7 +91,7 @@ public XQMap item(final QueryContext qc, final InputInfo ii) throws QueryExcepti
if(!isSupported(version)) throw MODULE_XQUERY_VERSION_X.get(info, version);
}
- final QueryContext mqc = new QueryContext(qc);
+ final QueryContext mqc = new QueryContext(qc, null);
for(final byte[] uri : qc.modDeclared) mqc.modDeclared.put(uri, qc.modDeclared.get(uri));
int nParsed = 0;
final Value ctx = opt.get(CONTEXT_ITEM);
diff --git a/basex-core/src/main/java/org/basex/query/func/xquery/XQueryEval.java b/basex-core/src/main/java/org/basex/query/func/xquery/XQueryEval.java
index 009db09000..3fdd891a57 100644
--- a/basex-core/src/main/java/org/basex/query/func/xquery/XQueryEval.java
+++ b/basex-core/src/main/java/org/basex/query/func/xquery/XQueryEval.java
@@ -75,7 +75,7 @@ final Value eval(final IOContent query, final boolean updating, final QueryConte
if(!user.has(perm)) throw XQUERY_PERMREQUIRED_X.get(info, perm);
Timer to = null;
- try(QueryContext qctx = new QueryContext(qc)) {
+ try(QueryContext qctx = new QueryContext(qc, null)) {
qctx.user = new User(user).permission(perm);
// limit memory consumption: enforce garbage collection and calculate usage
diff --git a/basex-core/src/main/java/org/basex/query/func/xquery/XQueryTask.java b/basex-core/src/main/java/org/basex/query/func/xquery/XQueryTask.java
index 88d58d9d60..4d3ea71e80 100644
--- a/basex-core/src/main/java/org/basex/query/func/xquery/XQueryTask.java
+++ b/basex-core/src/main/java/org/basex/query/func/xquery/XQueryTask.java
@@ -45,7 +45,7 @@ protected Value compute() {
final int size = end - start;
if(size == 1) {
// perform the work
- try(QueryContext qc = new QueryContext(tc.qc)) {
+ try(QueryContext qc = new QueryContext(tc.qc, tc.qc.ns)) {
return tc.funcs.get(start).invoke(qc, tc.info);
} catch(final QueryException ex) {
if(tc.errors) {
diff --git a/basex-core/src/test/java/org/basex/query/func/FnModuleTest.java b/basex-core/src/test/java/org/basex/query/func/FnModuleTest.java
index 70acae56b6..c9459d229e 100644
--- a/basex-core/src/test/java/org/basex/query/func/FnModuleTest.java
+++ b/basex-core/src/test/java/org/basex/query/func/FnModuleTest.java
@@ -2003,6 +2003,13 @@ public final class FnModuleTest extends SandboxTest {
query(func.args("x", " { 'content': 'module namespace x=\"x\";\ndeclare variable $x:x := 1;', "
+ "'xquery-version': 4.0 }") + "?variables?#Q{x}x", 1);
+ // GH-2640
+ error("{\n"
+ + " load-xquery-module(\"m\", {\n"
+ + " 'content': ``[module namespace m='m'; declare function m:f() {};]``\n"
+ + " })?functions?#Q{m}f?0()\n"
+ + "}", MODULE_STATIC_ERROR_X_X);
+
error(func.args(""), MODULE_URI_EMPTY);
error(func.args("x"), MODULE_NOT_FOUND_X);
error(func.args("x", " { 'content': '%@?$' }"), MODULE_STATIC_ERROR_X_X);
diff --git a/basex-core/src/test/java/org/basex/query/func/XQueryModuleTest.java b/basex-core/src/test/java/org/basex/query/func/XQueryModuleTest.java
index c10eed49ba..5e58cc5488 100644
--- a/basex-core/src/test/java/org/basex/query/func/XQueryModuleTest.java
+++ b/basex-core/src/test/java/org/basex/query/func/XQueryModuleTest.java
@@ -36,6 +36,17 @@ public final class XQueryModuleTest extends SandboxTest {
// GH-2332
query("try {" + func.args("declare function local:f() { local:f() }; local:f()") +
"} catch xquery:error { 'STOP' }", "STOP");
+
+ // GH-2640
+ query("element e { attribute { QName('', 'a') } {}, " + func.args("true()") + " }",
+ "true");
+ query("))\n"
+ + " ')}'/>", "");
}
/** Test method. */
@@ -183,6 +194,15 @@ public final class XQueryModuleTest extends SandboxTest {
+ " }")
+ "=> distinct-values()", "a b c d e");
+ // GH-2640: fork-join inherits parent dynamic namespace context
+ query(" count()\n"
+ + " }'\n"
+ + "/>", "");
+
// optimizations
check(func.args(" ()"), "", empty());
check(func.args(" false#0"), false, root(DynFuncCall.class));