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));