Index: modeshape-graph/src/main/java/org/modeshape/graph/query/parse/SqlQueryParser.java
===================================================================
--- modeshape-graph/src/main/java/org/modeshape/graph/query/parse/SqlQueryParser.java (revision 1903)
+++ modeshape-graph/src/main/java/org/modeshape/graph/query/parse/SqlQueryParser.java (working copy)
@@ -712,7 +712,7 @@ public class SqlQueryParser implements QueryParser {
tokens.consume(',');
// Followed by the full text search expression ...
- String expression = removeBracketsAndQuotes(tokens.consume());
+ String expression = removeBracketsAndQuotes(tokens.consume(), false); // don't remove nested quotes
Term term = parseFullTextSearchExpression(expression, tokens.previousPosition());
tokens.consume(")");
constraint = fullTextSearch(selectorName, propertyName, expression, term);
@@ -1261,23 +1261,40 @@ public class SqlQueryParser implements QueryParser {
}
/**
- * Remove any leading and trailing single-quotes, double-quotes, or square brackets from the supplied text.
+ * Remove all leading and trailing single-quotes, double-quotes, or square brackets from the supplied text. If multiple,
+ * properly-paired quotes or brackets are found, they will all be removed.
*
* @param text the input text; may not be null
* @return the text without leading and trailing brackets and quotes, or text
if there were no square brackets or
* quotes
*/
protected String removeBracketsAndQuotes( String text ) {
+ return removeBracketsAndQuotes(text, true);
+ }
+
+ /**
+ * Remove any leading and trailing single-quotes, double-quotes, or square brackets from the supplied text.
+ *
+ * @param text the input text; may not be null
+ * @param recursive true if more than one pair of quotes, double-quotes, or square brackets should be removed, or false if
+ * just the first pair should be removed
+ * @return the text without leading and trailing brackets and quotes, or text
if there were no square brackets or
+ * quotes
+ */
+ protected String removeBracketsAndQuotes( String text,
+ boolean recursive ) {
if (text.length() > 0) {
char firstChar = text.charAt(0);
switch (firstChar) {
case '\'':
case '"':
assert text.charAt(text.length() - 1) == firstChar;
- return removeBracketsAndQuotes(text.substring(1, text.length() - 1));
+ String removed = text.substring(1, text.length() - 1);
+ return recursive ? removeBracketsAndQuotes(removed, recursive) : removed;
case '[':
assert text.charAt(text.length() - 1) == ']';
- return removeBracketsAndQuotes(text.substring(1, text.length() - 1));
+ removed = text.substring(1, text.length() - 1);
+ return recursive ? removeBracketsAndQuotes(removed, recursive) : removed;
}
}
return text;
Index: modeshape-jcr/src/main/java/org/modeshape/jcr/xpath/XPathToQueryTranslator.java
===================================================================
--- modeshape-jcr/src/main/java/org/modeshape/jcr/xpath/XPathToQueryTranslator.java (revision 1903)
+++ modeshape-jcr/src/main/java/org/modeshape/jcr/xpath/XPathToQueryTranslator.java (working copy)
@@ -499,9 +499,20 @@ public class XPathToQueryTranslator {
right = temp;
operator = operator.reverse();
}
- if (left instanceof AttributeNameTest) {
- AttributeNameTest attribute = (AttributeNameTest)left;
- String propertyName = nameFrom(attribute.getNameTest());
+ if (left instanceof NodeTest) {
+ NodeTest nodeTest = (NodeTest)left;
+ String propertyName = null;
+ if (nodeTest instanceof AttributeNameTest) {
+ AttributeNameTest attribute = (AttributeNameTest)left;
+ propertyName = nameFrom(attribute.getNameTest());
+ } else if (nodeTest instanceof NameTest) {
+ NameTest nameTest = (NameTest)left;
+ propertyName = nameFrom(nameTest);
+ } else {
+ throw new InvalidQueryException(query,
+ "Left hand side of a comparison must be a name test or attribute name test; therefore '"
+ + comparison + "' is not valid");
+ }
if (right instanceof Literal) {
String value = ((Literal)right).getValue();
where.propertyValue(tableName, propertyName).is(operator, value);
Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrQueryManagerTest.java
===================================================================
--- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrQueryManagerTest.java (revision 1903)
+++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrQueryManagerTest.java (working copy)
@@ -783,6 +783,48 @@ public class JcrQueryManagerTest {
assertResultsHaveColumns(result, "jcr:primaryType", "jcr:path", "jcr:score");
}
+ @FixFor( "MODE-790" )
+ @SuppressWarnings( "deprecation" )
+ @Test
+ public void shouldBeAbleToExecuteXPathQueryWithCompoundCriteria() throws Exception {
+ Query query = session.getWorkspace()
+ .getQueryManager()
+ .createQuery("/jcr:root/Cars//element(*,car:Car)[@car:year='2008' and jcr:contains(., '\"liter V 12\"')]",
+ Query.XPATH);
+ assertThat(query, is(notNullValue()));
+ QueryResult result = query.execute();
+ assertThat(result, is(notNullValue()));
+
+ assertResults(query, result, 1);
+ assertResultsHaveColumns(result,
+ "jcr:primaryType",
+ "jcr:path",
+ "jcr:score",
+ "jcr:created",
+ "jcr:createdBy",
+ "car:mpgCity",
+ "car:userRating",
+ "car:mpgHighway",
+ "car:engine",
+ "car:model",
+ "car:year",
+ "car:maker",
+ "car:lengthInInches",
+ "car:valueRating",
+ "car:wheelbaseInInches",
+ "car:msrp");
+
+ // Query again with a different criteria that should return no nodes ...
+ query = session.getWorkspace()
+ .getQueryManager()
+ .createQuery("/jcr:root/Cars//element(*,car:Car)[@car:year='2007' and jcr:contains(., '\"liter V 12\"')]",
+ Query.XPATH);
+ assertThat(query, is(notNullValue()));
+ result = query.execute();
+ assertThat(result, is(notNullValue()));
+ assertResults(query, result, 0);
+ }
+
@SuppressWarnings( "deprecation" )
@Test
public void shouldBeAbleToExecuteXPathQueryWithElementTestForChildrenOfRoot() throws RepositoryException {
Index: modeshape-jcr/src/test/java/org/modeshape/jcr/xpath/XPathToQueryTranslatorTest.java
===================================================================
--- modeshape-jcr/src/test/java/org/modeshape/jcr/xpath/XPathToQueryTranslatorTest.java (revision 1903)
+++ modeshape-jcr/src/test/java/org/modeshape/jcr/xpath/XPathToQueryTranslatorTest.java (working copy)
@@ -29,6 +29,7 @@ import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.modeshape.common.FixFor;
import org.modeshape.graph.ExecutionContext;
import org.modeshape.graph.query.model.QueryCommand;
import org.modeshape.graph.query.model.TypeSystem;
@@ -349,6 +350,34 @@ public class XPathToQueryTranslatorTest {
isSql("SELECT nodeSet1.[jcr:primaryType] FROM __ALLNODES__ as nodeSet1 WHERE PATH(nodeSet1) LIKE '%/InfinitiG37' AND nodeSet1.[foo:year] = '2008'"));
}
+ @FixFor( "MODE-790" )
+ @Test
+ public void shouldTranslateFromXPathContainingCompoundCriteria() {
+ assertThat(xpath("/jcr:root/Cars//element(*,car:Car)[@car:year='2008' and jcr:contains(., '\"liter V 12\"')]"),
+ isSql("SELECT * FROM [car:Car] WHERE (PATH([car:Car]) LIKE '/Cars/%' AND ([car:Car].[car:year] = '2008' AND CONTAINS([car:Car].*,'\"liter V 12\"')))"));
+ }
+
+ @FixFor( "MODE-790" )
+ @Test
+ public void shouldTranslateFromXPathContainingCompoundCriteria2() {
+ assertThat(xpath("/jcr:root/drools:repository/drools:package_area//element(*, drools:assetNodeType)[jcr:contains(., 'testQueryText*')]"),
+ isSql("SELECT * FROM [drools:assetNodeType] WHERE (PATH([drools:assetNodeType]) LIKE '/drools:repository/drools:package_area/%' AND CONTAINS([drools:assetNodeType].*,'testQueryText*'))"));
+ }
+
+ @FixFor( "MODE-790" )
+ @Test
+ public void shouldTranslateFromXPathContainingCompoundCriteria3() {
+ assertThat(xpath("/jcr:root/drools:repository/drools:package_area//element(*, drools:assetNodeType)[jcr:contains(., 'testQueryText*') and drools:archive = 'false']"),
+ isSql("SELECT * FROM [drools:assetNodeType] WHERE (PATH([drools:assetNodeType]) LIKE '/drools:repository/drools:package_area/%' AND CONTAINS([drools:assetNodeType].*,'testQueryText*') AND [drools:archive] = 'false')"));
+ }
+
+ @FixFor( "MODE-790" )
+ @Test
+ public void shouldTranslateFromXPathContainingCompoundCriteria4() {
+ assertThat(xpath("/jcr:root/drools:repository/drools:package_area//element(*, drools:assetNodeType)[jcr:contains(., 'testQueryText*') and @drools:archive = 'false']"),
+ isSql("SELECT * FROM [drools:assetNodeType] WHERE (PATH([drools:assetNodeType]) LIKE '/drools:repository/drools:package_area/%' AND CONTAINS([drools:assetNodeType].*,'testQueryText*') AND [drools:archive] = 'false')"));
+ }
+
@Test
public void shouldParseXPathExpressions() {
xpath("/jcr:root/a/b/c");