Skip to content

Commit

Permalink
[bugfix] allow module imports in one-off xqueries
Browse files Browse the repository at this point in the history
fixes #5525

- add functx to autodeploy for xquery tests
- add tests for one-off queries with module imports
  - of a registered module without location hint
  - of a module with location hint
- change XQueryContext to allow imports again
- change SourceFactory to work with contextPath set to "."
  • Loading branch information
line-o committed Oct 31, 2024
1 parent 9f881d7 commit 6c7b6b7
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public class SourceFactory {
&& ((location.startsWith("/db") && !Files.exists(Paths.get(firstPathSegment(location))))
|| (contextPath != null && contextPath.startsWith("/db") && !Files.exists(Paths.get(firstPathSegment(contextPath)))))) {
final XmldbURI pathUri;
if (contextPath == null) {
if (contextPath == null || ".".equals(contextPath)) {
pathUri = XmldbURI.create(location);
} else {
pathUri = XmldbURI.create(contextPath).append(location);
Expand Down
45 changes: 23 additions & 22 deletions exist-core/src/main/java/org/exist/xquery/XQueryContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -587,33 +587,34 @@ public Optional<ExistRepository> getRepository() {
return null;
}

// use the resolved file or return null
if (resolved != null) {

String location = "";

try {

// see if the src exists in the database and if so, use that instead
Source src = repo.get().resolveStoredXQueryModuleFromDb(getBroker(), resolved);
if (src != null) {
// NOTE(AR) set the location of the module to import relative to this module's load path - so that transient imports of the imported module will resolve correctly!
location = Paths.get(XmldbURI.create(moduleLoadPath).getCollectionPath()).relativize(Paths.get(((DBSource)src).getDocumentPath().getCollectionPath())).toString();
// use the resolved file
String location = "";
try {
// see if the src exists in the database and if so, use that instead
Source src = repo.get().resolveStoredXQueryModuleFromDb(getBroker(), resolved);
if (src == null) {
// fallback to load the source from the filesystem
src = new FileSource(resolved, false);
} else {
final String sourceCollection = ((DBSource)src).getDocumentPath().getCollectionPath();
if (moduleLoadPath.equals(".")) {
// module is a string passed to the xquery context, has therefore no location of its own
location = sourceCollection;
} else {
// else, fallback to the one from the filesystem
src = new FileSource(resolved, false);
// NOTE(AR) set the location of the module to import relative to this module's load path
// - so that transient imports of the imported module will resolve correctly!
final Path sourcePath = Paths.get(sourceCollection);
final Path mlp = Paths.get(XmldbURI.create(moduleLoadPath).getCollectionPath());
location = mlp.relativize(sourcePath).toString();
}
}

// build a module object from the source
final ExternalModule module = compileOrBorrowModule(namespace, prefix, location, src);
return module;
// build a module object from the source
return compileOrBorrowModule(namespace, prefix, location, src);

} catch (final PermissionDeniedException e) {
throw new XPathException(e.getMessage(), e);
}
} catch (final PermissionDeniedException | IllegalArgumentException e) {
throw new XPathException(e.getMessage(), e);
}

return null;
}

/**
Expand Down
20 changes: 20 additions & 0 deletions exist-core/src/test/java/org/exist/xquery/AbsolutePathTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.exist.test.XQueryCompilationTest;
import org.exist.xquery.value.IntegerValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.StringValue;
import org.junit.Test;

import javax.xml.transform.Source;
Expand Down Expand Up @@ -133,4 +134,23 @@ public void topLevelAbsolutePath() throws EXistException, PermissionDeniedExcept

assertThatXQResult(actual, equalTo(expected));
}

@Test
public void importLibraryWithoutLocation() throws EXistException, PermissionDeniedException {
final Sequence expected = new StringValue("xs:integer");

final String query = "import module namespace functx='http://www.functx.com';functx:atomic-type(4)";
final Either<XPathException, Sequence> actual = executeQuery(query);

assertThatXQResult(actual, equalTo(expected));
}
@Test
public void importLibraryFromLocation() throws EXistException, PermissionDeniedException {
final Sequence expected = new StringValue("xs:integer");

final String query = "import module namespace functx='http://www.functx.com' at '/db/system/repo/functx-1.0.1/functx/functx.xq';functx:atomic-type(4)";
final Either<XPathException, Sequence> actual = executeQuery(query);

assertThatXQResult(actual, equalTo(expected));
}
}
89 changes: 89 additions & 0 deletions exist-core/src/test/java/org/exist/xquery/ModuleImportTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.exist.xquery;

import com.evolvedbinary.j8fu.Either;
import org.exist.EXistException;
import org.exist.security.PermissionDeniedException;
import org.exist.test.XQueryCompilationTest;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.StringValue;
import org.junit.Test;

import static org.exist.test.XQueryAssertions.assertThatXQResult;
import static org.exist.test.XQueryAssertions.assertXQStaticError;
import static org.hamcrest.Matchers.equalTo;

/**
* Ensure library module imports work in one-off queries
* needs functx to be installed => conf.xml => triggers => autodeploy
*
* @author <a href="mailto:[email protected]">Juri Leino</a>
*/
public class ModuleImportTest extends XQueryCompilationTest {
@Test
public void importLibraryWithoutLocation() throws EXistException, PermissionDeniedException {
final Sequence expected = new StringValue("xs:integer");

final String query = "import module namespace functx='http://www.functx.com';" +
"functx:atomic-type(4)";
final Either<XPathException, Sequence> actual = executeQuery(query);

assertThatXQResult(actual, equalTo(expected));
}
@Test
public void importLibraryFromDbLocation() throws EXistException, PermissionDeniedException {
final Sequence expected = new StringValue("xs:integer");

final String query = "import module namespace functx='http://www.functx.com'" +
" at '/db/system/repo/functx-1.0.1/functx/functx.xq';" +
"functx:atomic-type(4)";
final Either<XPathException, Sequence> actual = executeQuery(query);

assertThatXQResult(actual, equalTo(expected));
}

@Test
public void importLibraryFromXMLDBLocation() throws EXistException, PermissionDeniedException {
final Sequence expected = new StringValue("xs:integer");

final String query = "import module namespace functx='http://www.functx.com'" +
" at 'xmldb:///db/system/repo/functx-1.0.1/functx/functx.xq';" +
"functx:atomic-type(4)";
final Either<XPathException, Sequence> actual = executeQuery(query);

assertThatXQResult(actual, equalTo(expected));
}

@Test
public void importLibraryFromExistXMLDBLocation() throws EXistException, PermissionDeniedException {
final Sequence expected = new StringValue("xs:integer");

final String query = "import module namespace functx='http://www.functx.com'" +
" at 'xmldb:exist:///db/system/repo/functx-1.0.1/functx/functx.xq';" +
"functx:atomic-type(4)";
final Either<XPathException, Sequence> actual = executeQuery(query);

assertThatXQResult(actual, equalTo(expected));
}

@Test
public void importLibraryFromUnknownLocation() throws EXistException, PermissionDeniedException {

final String query = "import module namespace functx='http://www.functx.com'" +
" at 'unknown:///db/system/repo/functx-1.0.1/functx/functx.xq';" +
"functx:atomic-type(4)";
final String expectedMessage = "error found while loading module functx: Source for module 'http://www.functx.com' not found module location hint URI 'unknown:///db/system/repo/functx-1.0.1/functx/functx.xq'.";

assertXQStaticError(ErrorCodes.XQST0059, -1,-1, expectedMessage, compileQuery(query));
}

@Test
public void importLibraryFromRelativeLocation() throws EXistException, PermissionDeniedException {
final String query = "import module namespace functx='http://www.functx.com'" +
" at './functx.xq';" +
"functx:atomic-type(4)";
final String expectedMessage = "error found while loading module functx: Source for module 'http://www.functx.com' not found module location hint URI './functx.xq'.";

assertXQStaticError(ErrorCodes.XQST0059, -1,-1, expectedMessage, compileQuery(query));
}

}
10 changes: 10 additions & 0 deletions exist-core/src/test/resources-filtered/conf.xml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,16 @@
<parameter name="mode" value="memory"/>
</trigger>

<!--
AutoDeploymentTrigger will install any .xar application package it finds
in the autodeploy directory unless the application has already been installed
in the db.
-->
<trigger class="org.exist.repo.AutoDeploymentTrigger">
<parameter name="ignore-autodeploy-system-property" value="true"/>
<parameter name="dir" value="${project.build.testOutputDirectory}/functx"/>
</trigger>

</triggers>
</startup>

Expand Down

0 comments on commit 6c7b6b7

Please sign in to comment.