Skip to content

Commit e2e0512

Browse files
committed
[bugfix] If the value for an external variable is sent to the REST API but the XQuery does not declare such an external variable, then raise an error.
Closes #67
1 parent ca26a59 commit e2e0512

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

exist-core/src/main/java/org/exist/http/RESTServer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,10 @@ private void declareExternalAndXQJVariables(final XQueryContext context,
15381538
q = new QName(localname, uri, XMLConstants.DEFAULT_NS_PREFIX);
15391539
}
15401540

1541+
if (!context.isExternalVariableDeclared(q)) {
1542+
throw new XPathException(ErrorCodes.W3CErrorCode.XPDY0002, "External variable " + q + " is not declared in the XQuery");
1543+
}
1544+
15411545
if (uri != null && prefix != null) {
15421546
context.declareNamespace(prefix, uri);
15431547
}

exist-core/src/main/java/org/exist/xquery/XQueryContext.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,14 @@ public void reset(final boolean keepGlobals) {
14641464
cachedUriCollectionResults.clear();
14651465

14661466
if (!keepGlobals) {
1467-
globalVariables.clear();
1467+
for (final Variable globalVariable : globalVariables.values()) {
1468+
if (globalVariable.isExternal()) {
1469+
if (globalVariable instanceof VariableImpl) {
1470+
((VariableImpl) globalVariable).destroy(this, null);
1471+
}
1472+
globalVariable.setValue(null);
1473+
}
1474+
}
14681475
}
14691476

14701477
if (dynamicOptions != null) {
@@ -1931,6 +1938,35 @@ public void undeclareGlobalVariable(final QName name) {
19311938
globalVariables.remove(name);
19321939
}
19331940

1941+
/**
1942+
* Determines if a Global External Variable is declared.
1943+
*
1944+
* @param variableName The name of the variable.
1945+
*
1946+
* @return true, if the variable is declared (in either this module or an imported module), or false if the variable is undeclared.
1947+
*/
1948+
public boolean isExternalVariableDeclared(final QName variableName) {
1949+
for (final Map.Entry<QName, Variable> mainGlobalVariable : globalVariables.entrySet()) {
1950+
if (mainGlobalVariable.getValue().isExternal() && mainGlobalVariable.getKey().equals(variableName)) {
1951+
return true;
1952+
}
1953+
}
1954+
1955+
for (final Module[] namespaceModules : modules.values()) {
1956+
for (final Module namespaceModule : namespaceModules) {
1957+
if (!namespaceModule.isInternalModule()) {
1958+
for (final VariableDeclaration libGlobalVariable : ((ExternalModule) namespaceModule).getVariableDeclarations()) {
1959+
if (libGlobalVariable.isExternal() && libGlobalVariable.getName().equals(variableName)) {
1960+
return true;
1961+
}
1962+
}
1963+
}
1964+
}
1965+
}
1966+
1967+
return false;
1968+
}
1969+
19341970
@Override
19351971
public Variable declareVariable(final String qname, final Object value) throws XPathException {
19361972
return declareVariable(qname, false, value);

exist-core/src/test/java/org/exist/http/RESTExternalVariableTest.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,57 @@ public class RESTExternalVariableTest {
9898
.elseUse(ElementSelectors.byName)
9999
.build());
100100

101+
@Test
102+
public void queryPostWithExternalVariableNotSupplied() throws IOException {
103+
final String query =
104+
"<exist:query xmlns:exist=\"http://exist.sourceforge.net/NS/exist\" xmlns:sx=\"http://exist-db.org/xquery/types/serialized\" xmlns:" + TEST_PREFIX + "=\"" + TEST_NAMESPACE + "\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" wrap=\"yes\" typed=\"yes\">\n" +
105+
"\t<exist:text><![CDATA[\n" +
106+
"declare variable $local:my-variable as xs:string* external;\n" +
107+
"$local:my-variable\n" +
108+
"\t]]></exist:text>\n" +
109+
"</exist:query>\n";
110+
111+
final HttpResponse response = doPostWithAuth(getResourceUri(), query);
112+
final int resultStatusCode = response.getStatusLine()
113+
.getStatusCode();
114+
115+
assertEquals("Server returned response code: " + resultStatusCode, HttpStatus.BAD_REQUEST_400, resultStatusCode);
116+
117+
final String actual = readResponse(response.getEntity());
118+
assertThat(actual, CompareMatcher.isIdenticalTo("<exception><path>/db/test/test.xml</path><message>err:XPDY0002 The value of external variable: local:my-variable has not been set</message></exception>"));
119+
}
120+
121+
@Test
122+
public void queryPostWithExternalVariableUndeclared() throws IOException {
123+
final String query =
124+
"<exist:query xmlns:exist=\"http://exist.sourceforge.net/NS/exist\" xmlns:sx=\"http://exist-db.org/xquery/types/serialized\" xmlns:" + TEST_PREFIX + "=\"" + TEST_NAMESPACE + "\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" wrap=\"yes\" typed=\"yes\">\n" +
125+
"\t<exist:variables>\n" +
126+
"\t\t<exist:variable>\n" +
127+
"\t\t\t<exist:qname><exist:prefix>local</exist:prefix><exist:localname>my-variable</exist:localname></exist:qname>\n" +
128+
"\t\t\t<sx:sequence><sx:value type=\"xs:string\">hello</sx:value></sx:sequence>\n" +
129+
"\t\t</exist:variable>\n" +
130+
"\t\t<exist:variable>\n" +
131+
"\t\t\t<exist:qname><exist:prefix>local</exist:prefix><exist:localname>other-variable</exist:localname></exist:qname>\n" +
132+
"\t\t\t<sx:sequence><sx:value type=\"xs:string\">goodbye</sx:value></sx:sequence>\n" +
133+
"\t\t</exist:variable>\n" +
134+
"\t</exist:variables>\n" +
135+
"\t<exist:text><![CDATA[\n" +
136+
"declare variable $local:my-variable as xs:string* external;\n" +
137+
"$local:my-variable\n" +
138+
"\t]]></exist:text>\n" +
139+
"</exist:query>\n";
140+
141+
final HttpResponse response = doPostWithAuth(getResourceUri(), query);
142+
final int resultStatusCode = response.getStatusLine()
143+
.getStatusCode();
144+
145+
assertEquals("Server returned response code: " + resultStatusCode, HttpStatus.BAD_REQUEST_400, resultStatusCode);
146+
147+
final String actual = readResponse(response.getEntity());
148+
149+
assertThat(actual, CompareMatcher.isIdenticalTo("<exception><path>/db/test/test.xml</path><message>err:XPDY0002 External variable local:other-variable is not declared in the XQuery</message></exception>"));
150+
}
151+
101152
@Test
102153
public void queryPostWithExternalVariableUntypedNotSupplied() throws IOException {
103154
queryPostWithExternalVariable(HttpStatus.BAD_REQUEST_400, null, (ExternalVariableValueRep[]) null);
@@ -1592,7 +1643,8 @@ private void queryPostWithExternalVariable(final Tuple2<Integer, String> expecte
15921643

15931644
assertEquals("Server returned response code: " + resultStatusCode, (int) expectedResponse._1, resultStatusCode);
15941645

1595-
final String actual = readResponse(response.getEntity());
1646+
String actual = readResponse(response.getEntity());
1647+
actual = actual.replaceFirst("\\s*\\[source:[^\\]]*\\]</message></exception>$", "</message></exception>"); // NOTE(AR) remove any source information from the actual response
15961648

15971649
@Nullable final String expected;
15981650
if (expectedResponse._1 == HttpStatus.OK_200) {

0 commit comments

Comments
 (0)