Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow module imports in one-off xqueries #5529

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to upset anyone, but I just wanted to point out that this is the wrong approach to fixing this issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fixing an additional bug I found while creating tests:

see importLibraryFromDbLocation and importLibraryFromXMLDBLocation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I understood that, but it still is the wrong approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I understood that, but it still is the wrong approach.

@adamretter Could you explain why you think this is the wrong approach and how do you think it should be solved correctly?

pathUri = XmldbURI.create(location);
} else {
pathUri = XmldbURI.create(contextPath).append(location);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,22 @@
import org.exist.xquery.value.Sequence;
import org.junit.ClassRule;

import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;

import static com.evolvedbinary.j8fu.Either.Left;
import static com.evolvedbinary.j8fu.Either.Right;
import static com.ibm.icu.impl.Assert.fail;

/**
* Base class for test suites testing XQuery compilation
* @author <a href="mailto:[email protected]">Juri Leino</a>
*/
public abstract class XQueryCompilationTest {
@ClassRule
public static final ExistEmbeddedServer server = new ExistEmbeddedServer(true, true);

@ClassRule
public static ExistEmbeddedServer server = new ExistEmbeddedServer(true, true);
protected static Either<XPathException, CompiledXQuery> compileQuery(final String string) throws EXistException, PermissionDeniedException {
final BrokerPool pool = server.getBrokerPool();
final XQuery xqueryService = pool.getXQueryService();
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 (".".equals(moduleLoadPath)) {
// 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 collectionPath = Paths.get(XmldbURI.create(moduleLoadPath).getCollectionPath());
final Path sourcePath = Paths.get(sourceCollection);
location = collectionPath.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
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,5 @@ public void topLevelAbsolutePath() throws EXistException, PermissionDeniedExcept

assertThatXQResult(actual, equalTo(expected));
}

}
162 changes: 162 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,162 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* [email protected]
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.xquery;

import com.evolvedbinary.j8fu.Either;

import org.exist.EXistException;
import org.exist.security.PermissionDeniedException;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.test.ExistEmbeddedServer;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.StringValue;

import org.junit.ClassRule;
import org.junit.Test;

import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;

import static com.evolvedbinary.j8fu.Either.Left;
import static com.evolvedbinary.j8fu.Either.Right;
import static com.ibm.icu.impl.Assert.fail;
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 {
@ClassRule
public static ExistEmbeddedServer server = new ExistEmbeddedServer(null, getConfigFile(), null, false, true);

protected static Either<XPathException, CompiledXQuery> compileQuery(final String string) throws EXistException, PermissionDeniedException {
final BrokerPool pool = server.getBrokerPool();
final XQuery xqueryService = pool.getXQueryService();
try (final DBBroker broker = pool.getBroker()) {
try {
return Right(xqueryService.compile(new XQueryContext(broker.getDatabase()), string));
} catch (final XPathException e) {
return Left(e);
}
}
}

protected static Either<XPathException, Sequence> executeQuery(final String string) throws EXistException, PermissionDeniedException {
final BrokerPool pool = server.getBrokerPool();
final XQuery xqueryService = pool.getXQueryService();
try (final DBBroker broker = pool.getBroker()) {
try {
return Right(xqueryService.execute(broker, string, null));
} catch (final XPathException e) {
return Left(e);
}
}
}

private static Path getConfigFile() {
final ClassLoader loader = ModuleImportTest.class.getClassLoader();
final char separator = System.getProperty("file.separator").charAt(0);
final String packagePath = ModuleImportTest.class.getPackage().getName().replace('.', separator);

try {
return Paths.get(loader.getResource(packagePath + separator + "conf.xml").toURI());
} catch (final URISyntaxException e) {
fail(e);
return null;
}
}

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

}
Loading
Loading