Skip to content

Commit 1090114

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 dcccf8f commit 1090114

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
@@ -1515,6 +1515,10 @@ private void declareExternalAndXQJVariables(final XQueryContext context,
15151515
q = new QName(localname, uri, XMLConstants.DEFAULT_NS_PREFIX);
15161516
}
15171517

1518+
if (!context.isExternalVariableDeclared(q)) {
1519+
throw new XPathException(ErrorCodes.W3CErrorCode.XPDY0002, "External variable " + q + " is not declared in the XQuery");
1520+
}
1521+
15181522
if (uri != null && prefix != null) {
15191523
context.declareNamespace(prefix, uri);
15201524
}

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

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

15371537
if (!keepGlobals) {
1538-
globalVariables.clear();
1538+
for (final Variable globalVariable : globalVariables.values()) {
1539+
if (globalVariable.isExternal()) {
1540+
if (globalVariable instanceof VariableImpl) {
1541+
((VariableImpl) globalVariable).destroy(this, null);
1542+
}
1543+
globalVariable.setValue(null);
1544+
}
1545+
}
15391546
}
15401547

15411548
if (dynamicOptions != null) {
@@ -1923,6 +1930,35 @@ public void undeclareGlobalVariable(final QName name) {
19231930
globalVariables.remove(name);
19241931
}
19251932

1933+
/**
1934+
* Determines if a Global External Variable is declared.
1935+
*
1936+
* @param variableName The name of the variable.
1937+
*
1938+
* @return true, if the variable is declared (in either this module or an imported module), or false if the variable is undeclared.
1939+
*/
1940+
public boolean isExternalVariableDeclared(final QName variableName) {
1941+
for (final Map.Entry<QName, Variable> mainGlobalVariable : globalVariables.entrySet()) {
1942+
if (mainGlobalVariable.getValue().isExternal() && mainGlobalVariable.getKey().equals(variableName)) {
1943+
return true;
1944+
}
1945+
}
1946+
1947+
for (final Module[] namespaceModules : modules.values()) {
1948+
for (final Module namespaceModule : namespaceModules) {
1949+
if (!namespaceModule.isInternalModule()) {
1950+
for (final VariableDeclaration libGlobalVariable : ((ExternalModule) namespaceModule).getVariableDeclarations()) {
1951+
if (libGlobalVariable.isExternal() && libGlobalVariable.getName().equals(variableName)) {
1952+
return true;
1953+
}
1954+
}
1955+
}
1956+
}
1957+
}
1958+
1959+
return false;
1960+
}
1961+
19261962
@Override
19271963
public Variable declareVariable(final String qname, final Object value) throws XPathException {
19281964
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)