/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr.query.parse; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertThat; import java.math.BigDecimal; import java.util.Comparator; import java.util.Set; import javax.jcr.Value; import javax.jcr.query.qom.EquiJoinCondition; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.modeshape.common.FixFor; import org.modeshape.common.text.TokenStream; import org.modeshape.jcr.ExecutionContext; import org.modeshape.jcr.JcrValueFactory; import org.modeshape.jcr.cache.NodeKey; import org.modeshape.jcr.query.JcrTypeSystem; import org.modeshape.jcr.query.model.And; import org.modeshape.jcr.query.model.Comparison; import org.modeshape.jcr.query.model.Constraint; import org.modeshape.jcr.query.model.DescendantNode; import org.modeshape.jcr.query.model.DescendantNodeJoinCondition; import org.modeshape.jcr.query.model.DynamicOperand; import org.modeshape.jcr.query.model.Join; import org.modeshape.jcr.query.model.JoinCondition; import org.modeshape.jcr.query.model.JoinType; import org.modeshape.jcr.query.model.Literal; import org.modeshape.jcr.query.model.LiteralValue; import org.modeshape.jcr.query.model.NamedSelector; import org.modeshape.jcr.query.model.NodePath; import org.modeshape.jcr.query.model.Not; import org.modeshape.jcr.query.model.Or; import org.modeshape.jcr.query.model.Order; import org.modeshape.jcr.query.model.Ordering; import org.modeshape.jcr.query.model.PropertyExistence; import org.modeshape.jcr.query.model.PropertyValue; import org.modeshape.jcr.query.model.Query; import org.modeshape.jcr.query.model.QueryCommand; import org.modeshape.jcr.query.model.SameNodeJoinCondition; import org.modeshape.jcr.query.model.SelectorName; import org.modeshape.jcr.query.model.SetQuery; import org.modeshape.jcr.query.model.Source; import org.modeshape.jcr.query.model.StaticOperand; import org.modeshape.jcr.query.model.TypeSystem; import org.modeshape.jcr.value.BinaryValue; import org.modeshape.jcr.value.Name; import org.modeshape.jcr.value.Path; import org.modeshape.jcr.value.PropertyType; import org.modeshape.jcr.value.Reference; /** * */ public class JcrSql2QueryParserTest { /** * Note that this ValueFactory is a simple mock that just returns null for each of its <code>createX()</code> methods, since * the {@link Value} objects are just placed into the {@link LiteralValue} objects, and we never call * {@link LiteralValue#getLiteralValue()} in these tests. */ @Mock private JcrValueFactory valueFactory; private JcrTypeSystem typeSystem; private JcrSql2QueryParser parser; private Query query; @Before public void beforeEach() { MockitoAnnotations.initMocks(this); typeSystem = new MockJcrTypeSystem(valueFactory); parser = new JcrSql2QueryParser(); } // ---------------------------------------------------------------------------------------------------------------- // parseQuery // ---------------------------------------------------------------------------------------------------------------- @Test public void shouldParseNominalQueries() { parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived1' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived1' AND PATH() LIKE '/drools:repository/drools:package_area/%'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived2' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived%' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived2' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived1' AND PATH() LIKE '/drools:repository/drools:package_area/%'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived1' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived%' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNameArchived%' AND PATH() LIKE '/drools:repository/drools:package_area/%'"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testRestPost/assets[%]/%' and ( [drools:format]='drl' OR [drools:format]='xls' ) AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testRestDelete/assets[%]/%' and [drools:format]='drl' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testRestDelete/assets[%]/%' and [drools:archive] = 'true' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testRestDelete/assets[%]/%' and [drools:format]='drl' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testPackageSnapshot/assets[%]/%' and [drools:format]='drl' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testPackageSnapshot/assets[%]/%' and [drools:format]='drl' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:packagesnapshot_area/testPackageSnapshot/PROD 2.0/assets[%]/%' and [drools:format]='drl' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/searchByFormat/assets[%]/%' and [drools:format]='xyz' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/searchByFormat/assets[%]/%' and [drools:format]='xyz' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/searchByFormat/assets[%]/%' and ( [drools:format]='xyz' OR [drools:format]='ABC' ) AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/globalArea/assets[%]/%' and [drools:format]='testSearchSharedAssetByFormat' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/org.drools.archivedtest/assets[%]/%' and [drools:archive] = 'true' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/org.drools.archivedtest/assets[%]/%' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testExcludeAssetTypes/assets[%]/%' and not [drools:format]='drl' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testExcludeAssetTypes/assets[%]/%' and not ( [drools:format]='drl' OR [drools:format]='wang' ) AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/testExcludeAssetTypes/assets[%]/%' and not ( [drools:format]='drl' OR [drools:format]='xls' ) AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNamex1' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNamex2' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNamex%' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNamex2' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE [drools:title] LIKE 'findRulesByNamex%' AND PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:subject] LIKE 'testQueryXXX42' AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND [drools:subject] LIKE 'testQueryXXX42' AND [drools:source] LIKE 'database'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND ([drools:subject] LIKE 'testQueryXXX42' OR [drools:subject] LIKE 'wankle') AND ([drools:source] LIKE 'database' OR [drools:source] LIKE 'wankle') AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND ([drools:source] LIKE 'database' OR [drools:source] LIKE 'wankle') AND [drools:archive] = 'false'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND ([drools:subject] LIKE 'testQueryXXX42' OR [drools:subject] LIKE 'wankle') AND ([drools:source] LIKE 'database' OR [drools:source] LIKE 'wankle') AND [drools:archive] = 'false' AND [jcr:created] > '1974-07-10T00:00:00.000-05:00' AND [jcr:created] < '3074-07-10T00:00:00.000-05:00'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND ([drools:subject] LIKE 'testQueryXXX42' OR [drools:subject] LIKE 'wankle') AND ([drools:source] LIKE 'database' OR [drools:source] LIKE 'wankle') AND [drools:archive] = 'false' AND [jcr:created] > '1974-07-10T00:00:00.000-05:00'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND ([drools:subject] LIKE 'testQueryXXX42' OR [drools:subject] LIKE 'wankle') AND ([drools:source] LIKE 'database' OR [drools:source] LIKE 'wankle') AND [drools:archive] = 'false' AND [jcr:created] < '3074-07-10T00:00:00.000-05:00'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND ([drools:subject] LIKE 'testQueryXXX42' OR [drools:subject] LIKE 'wankle') AND ([drools:source] LIKE 'database' OR [drools:source] LIKE 'wankle') AND [drools:archive] = 'false' AND [jcr:created] > '3074-07-10T00:00:00.000-05:00'"); parse("SELECT [drools:title], [drools:description], [drools:archive] FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/%' AND ([drools:subject] LIKE 'testQueryXXX42' OR [drools:subject] LIKE 'wankle') AND ([drools:source] LIKE 'database' OR [drools:source] LIKE 'wankle') AND [drools:archive] = 'false' AND [jcr:created] < '1974-07-10T00:00:00.000-05:00'"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/globalArea/assets[%]/%' and [drools:format]='xyz' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("SELECT * FROM [drools:assetNodeType] WHERE PATH() LIKE '/drools:repository/drools:package_area/globalArea/assets[%]/%' and [drools:format]='xyz' AND [drools:archive] = 'false' ORDER BY [drools:title]"); parse("select * from [mgnl:content] where PATH() like '/modules/%/templates'"); } @Test public void shouldParseQueriesUsedInJcrTckTests() { parse("SELECT * FROM [nt:unstructured] JOIN [mix:referenceable] ON ISSAMENODE([nt:unstructured],[mix:referenceable]) WHERE PATH([nt:unstructured]) LIKE '/testroot/%'"); parse("SELECT * FROM [nt:unstructured] JOIN [nt:base] ON ISSAMENODE([nt:base], [nt:unstructured]) WHERE PATH([nt:unstructured]) LIKE '/testroot/%'"); parse("SELECT * FROM [nt:base] JOIN [mix:referenceable] ON ISSAMENODE([nt:base], [mix:referenceable]) WHERE PATH([nt:base]) LIKE '/testroot/%'"); parse("SELECT * FROM [nt:unstructured] JOIN [mix:referenceable] ON ISSAMENODE([nt:unstructured], [mix:referenceable]) WHERE PATH([nt:unstructured]) LIKE '/testroot/%'"); parse("SELECT prop1 FROM [nt:unstructured] WHERE prop2 IN ('two') AND prop1 = 'existence' AND PATH() LIKE '/testroot/%'"); } @Test public void shouldParseSelectStarFromSingleSourceWithWhereContainingPathLikeConstraint() { query = parse("SELECT * FROM [mgnl:content] WHERE PATH() LIKE '/modules/%/templates'"); assertThat(query.source(), is(instanceOf(NamedSelector.class))); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... NamedSelector selector = (NamedSelector)query.source(); assertThat(selector.name(), is(selectorName("mgnl:content"))); assertThat(selector.aliasOrName(), is(selectorName("mgnl:content"))); assertThat(selector.alias(), is(nullValue())); // WHERE ... Comparison comparison = isComparison(query.constraint()); assertThat(comparison.getOperand1(), is((DynamicOperand)nodePath(selectorName("mgnl:content")))); assertThat(comparison.getOperand2(), is((StaticOperand)literal("/modules/%/templates"))); } @Test public void shouldParseSelectStarFromSingleSourceWithWhereContainingTwoPathLikeConstraints() { query = parse("SELECT * FROM [mgnl:content] WHERE PATH() LIKE '/modules/%/templates' or PATH() like '/modules/%/other'"); assertThat(query.source(), is(instanceOf(NamedSelector.class))); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... NamedSelector selector = (NamedSelector)query.source(); assertThat(selector.name(), is(selectorName("mgnl:content"))); assertThat(selector.aliasOrName(), is(selectorName("mgnl:content"))); assertThat(selector.alias(), is(nullValue())); // WHERE ... Or and = isOr(query.constraint()); Comparison comparison1 = isComparison(and.left()); assertThat(comparison1.getOperand1(), is((DynamicOperand)nodePath(selectorName("mgnl:content")))); assertThat(comparison1.getOperand2(), is((StaticOperand)literal("/modules/%/templates"))); Comparison comparison2 = isComparison(and.right()); assertThat(comparison2.getOperand1(), is((DynamicOperand)nodePath(selectorName("mgnl:content")))); assertThat(comparison2.getOperand2(), is((StaticOperand)literal("/modules/%/other"))); } @Test public void shouldParseSelectStarFromTwoJoinedSourcesWithWhereContainingJoinCriteria() { query = parse("SELECT * FROM [mgnl:content] JOIN [acme:stuff] ON ISSAMENODE([mgnl:content],[acme:stuff])"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... Join join = isJoin(query.source()); assertThat(join.getLeft(), is((Source)namedSelector(selectorName("mgnl:content")))); assertThat(join.getRight(), is((Source)namedSelector(selectorName("acme:stuff")))); assertThat(join.type(), is(JoinType.INNER)); SameNodeJoinCondition joinCondition = isSameNodeJoinCondition(join.getJoinCondition()); assertThat(joinCondition.selector1Name(), is(selectorName("mgnl:content"))); assertThat(joinCondition.selector2Name(), is(selectorName("acme:stuff"))); assertThat(joinCondition.getSelector2Path(), is(nullValue())); // WHERE ... assertThat(query.constraint(), is(nullValue())); } @Test public void shouldParseSelectStarFromThreeJoinedSourcesWithWhereContainingJoinCriteria() { query = parse("SELECT * FROM [mgnl:content] JOIN [acme:stuff] ON ISSAMENODE([mgnl:content],[acme:stuff]) JOIN [foo:bar] ON ISSAMENODE([mgnl:content],[foo:bar])"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... Join join = isJoin(query.source()); Join join2 = isJoin(join.getLeft()); assertThat(join2.getLeft(), is((Source)namedSelector(selectorName("mgnl:content")))); assertThat(join2.getRight(), is((Source)namedSelector(selectorName("acme:stuff")))); assertThat(join2.type(), is(JoinType.INNER)); SameNodeJoinCondition joinCondition2 = isSameNodeJoinCondition(join2.getJoinCondition()); assertThat(joinCondition2.selector1Name(), is(selectorName("mgnl:content"))); assertThat(joinCondition2.selector2Name(), is(selectorName("acme:stuff"))); assertThat(joinCondition2.getSelector2Path(), is(nullValue())); assertThat(join.getRight(), is((Source)namedSelector(selectorName("foo:bar")))); assertThat(join.type(), is(JoinType.INNER)); SameNodeJoinCondition joinCondition = isSameNodeJoinCondition(join.getJoinCondition()); assertThat(joinCondition.selector1Name(), is(selectorName("mgnl:content"))); assertThat(joinCondition.selector2Name(), is(selectorName("foo:bar"))); assertThat(joinCondition.getSelector2Path(), is(nullValue())); // WHERE ... assertThat(query.constraint(), is(nullValue())); } @Test public void shouldParseSelectStarFromEquijoinAndAdditionalCriteria() { query = parse("SELECT * FROM [modetest:queryable] JOIN [mix:referenceable] ON ISSAMENODE([modetest:queryable],[mix:referenceable]) WHERE PATH([modetest:queryable]) LIKE '/testroot/someQueryableNodeD/%'"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... Join join = isJoin(query.source()); assertThat(join.getLeft(), is((Source)namedSelector(selectorName("modetest:queryable")))); assertThat(join.getRight(), is((Source)namedSelector(selectorName("mix:referenceable")))); assertThat(join.type(), is(JoinType.INNER)); SameNodeJoinCondition joinCondition = isSameNodeJoinCondition(join.getJoinCondition()); assertThat(joinCondition.selector1Name(), is(selectorName("modetest:queryable"))); assertThat(joinCondition.selector2Name(), is(selectorName("mix:referenceable"))); assertThat(joinCondition.getSelector2Path(), is(nullValue())); // WHERE ... Comparison comparison = isComparison(query.constraint()); assertThat(comparison.getOperand1(), is((DynamicOperand)nodePath(selectorName("modetest:queryable")))); assertThat(comparison.getOperand2(), is((StaticOperand)literal("/testroot/someQueryableNodeD/%"))); } @Test public void shouldParseSelectWithOrderByClause() { query = parse("SELECT [car:model] FROM [car:Car] WHERE [car:model] IS NOT NULL ORDER BY [car:model] ASC"); // SELECT car:model ... assertThat(query.columns().size(), is(1)); assertThat(query.columns().get(0).selectorName(), is(selectorName("car:Car"))); assertThat(query.columns().get(0).getColumnName(), is("car:model")); assertThat(query.columns().get(0).getPropertyName(), is("car:model")); // FROM ... NamedSelector selector = (NamedSelector)query.source(); assertThat(selector.name(), is(selectorName("car:Car"))); assertThat(selector.aliasOrName(), is(selectorName("car:Car"))); assertThat(selector.alias(), is(nullValue())); // WHERE ... PropertyExistence constraint = isPropertyExistence(query.constraint()); assertThat(constraint.getPropertyName(), is("car:model")); assertThat(constraint.selectorName(), is(selectorName("car:Car"))); // ORDER BY ... assertThat(query.orderings().size(), is(1)); Ordering ordering = query.orderings().get(0); assertThat(ordering.order(), is(Order.ASCENDING)); assertThat(ordering.getOperand(), is((DynamicOperand)propertyValue(selectorName("car:Car"), "car:model"))); } /** * Tests that the child nodes (but no grandchild nodes) are returned. */ @Test public void shouldParseSelectWithChildAxisCriteria() { query = parse("SELECT * FROM [nt:base] WHERE PATH() LIKE '/a/b/%' AND NOT PATH() LIKE '/a/b/%/%'"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... NamedSelector selector = (NamedSelector)query.source(); assertThat(selector.name(), is(selectorName("nt:base"))); assertThat(selector.aliasOrName(), is(selectorName("nt:base"))); assertThat(selector.alias(), is(nullValue())); // WHERE ... And and = isAnd(query.constraint()); Comparison comparison1 = isComparison(and.left()); assertThat(comparison1.getOperand1(), is((DynamicOperand)nodePath(selectorName("nt:base")))); assertThat(comparison1.getOperand2(), is((StaticOperand)literal("/a/b/%"))); Not not = isNot(and.right()); Comparison comparison2a = isComparison(not.getConstraint()); assertThat(comparison2a.getOperand1(), is((DynamicOperand)nodePath(selectorName("nt:base")))); assertThat(comparison2a.getOperand2(), is((StaticOperand)literal("/a/b/%/%"))); } @Test public void shouldParseDescendantNodeJoinWithNoCriteria() { query = parse("select * from [lom:Metadata] as lom join [lom:LangString] as lang on isdescendantnode(lang,lom)"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... Join join = isJoin(query.source()); assertThat(join.getLeft(), is((Source)namedSelector(selectorName("lom:Metadata"), selectorName("lom")))); assertThat(join.getRight(), is((Source)namedSelector(selectorName("lom:LangString"), selectorName("lang")))); assertThat(join.type(), is(JoinType.INNER)); DescendantNodeJoinCondition joinCondition = isDescendantNodeJoinCondition(join.getJoinCondition()); assertThat(joinCondition.ancestorSelectorName(), is(selectorName("lom"))); assertThat(joinCondition.descendantSelectorName(), is(selectorName("lang"))); } @FixFor( "MODE-1366" ) @Test public void shouldParseSelectStarFromSourceWithDescendantNodeCriteriaWithNoSnsIndexInPath() { query = parse("SELECT * FROM [car:Car] WHERE ISDESCENDANTNODE([car:Car],[/foo/bar])"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... NamedSelector selector = (NamedSelector)query.source(); assertThat(selector.name(), is(selectorName("car:Car"))); assertThat(selector.aliasOrName(), is(selectorName("car:Car"))); assertThat(selector.alias(), is(nullValue())); // WHERE ... DescendantNode desc = isDescendantNodeCriteria(query.constraint()); assertThat(desc.getSelectorName(), is("car:Car")); assertThat(desc.getAncestorPath(), is("/foo/bar")); } @FixFor( "MODE-1366" ) @Test public void shouldParseSelectStarFromSourceWithDescendantNodeCriteriaWithSnsIndexInPathAndSquareBrackets() { query = parse("SELECT * FROM [car:Car] WHERE ISDESCENDANTNODE([car:Car],[/foo/bar[2]])"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... NamedSelector selector = (NamedSelector)query.source(); assertThat(selector.name(), is(selectorName("car:Car"))); assertThat(selector.aliasOrName(), is(selectorName("car:Car"))); assertThat(selector.alias(), is(nullValue())); // WHERE ... DescendantNode desc = isDescendantNodeCriteria(query.constraint()); assertThat(desc.getSelectorName(), is("car:Car")); assertThat(desc.getAncestorPath(), is("/foo/bar[2]")); } @FixFor( "MODE-1366" ) @Test public void shouldParseSelectStarFromSourceWithDescendantNodeCriteriaWithSnsIndexInPath() { query = parse("SELECT * FROM [car:Car] WHERE ISDESCENDANTNODE([car:Car],'/foo/bar[2]')"); // SELECT * ... assertThat(query.columns().isEmpty(), is(true)); // FROM ... NamedSelector selector = (NamedSelector)query.source(); assertThat(selector.name(), is(selectorName("car:Car"))); assertThat(selector.aliasOrName(), is(selectorName("car:Car"))); assertThat(selector.alias(), is(nullValue())); // WHERE ... DescendantNode desc = isDescendantNodeCriteria(query.constraint()); assertThat(desc.getSelectorName(), is("car:Car")); assertThat(desc.getAncestorPath(), is("/foo/bar[2]")); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedPathInSelect() { query = parse("select [jcr:primaryType], [jcr:path] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeNameInSelect() { query = parse("select [jcr:primaryType], [jcr:name] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeLocalNameInSelect() { query = parse("select [jcr:primaryType], [jcr:localName] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeDepthInSelect() { query = parse("select [jcr:primaryType], [jcr:depth] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeScoreInSelect() { query = parse("select [jcr:primaryType], [jcr:score] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedPathInSelect() { query = parse("select [nt:base].[jcr:primaryType], [nt:base].[jcr:path] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeNameInSelect() { query = parse("select [nt:base].[jcr:primaryType], [nt:base].[jcr:name] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeLocalNameInSelect() { query = parse("select [nt:base].[jcr:primaryType], [nt:base].[jcr:localName] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeDepthInSelect() { query = parse("select [nt:base].[jcr:primaryType], [nt:base].[jcr:depth] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeScoreInSelect() { query = parse("select [nt:base].[jcr:primaryType], [nt:base].[jcr:score] FROM [nt:base]"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedPathInCriteria() { query = parse("select [jcr:primaryType] FROM [nt:base] WHERE [jcr:path] = '/some/path'"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeNameInCriteria() { query = parse("select [jcr:primaryType] FROM [nt:base] WHERE [jcr:path] = 'mode:nodeName'"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeLocalNameInCriteria() { query = parse("select [jcr:primaryType] FROM [nt:base] WHERE [jcr:localName] = 'nodeName'"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeDepthInCriteria() { query = parse("select [jcr:primaryType] FROM [nt:base] WHERE [jcr:depth] = 2"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithUnqualifiedNodeScoreInCriteria() { query = parse("select [jcr:primaryType] FROM [nt:base] WHERE [jcr:score] <= 2.0"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedPathInCriteria() { query = parse("select [nt:base].[jcr:primaryType] FROM [nt:base] WHERE [nt:base].[jcr:path] = '/some/path'"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeNameInCriteria() { query = parse("select [nt:base].[jcr:primaryType] FROM [nt:base] WHERE [nt:base].[jcr:name] = 'mode:nodeName'"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeLocalNameInCriteria() { query = parse("select [nt:base].[jcr:primaryType] FROM [nt:base] WHERE [nt:base].[jcr:localName] = 'nodeName'"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeDepthInCriteria() { query = parse("select [nt:base].[jcr:primaryType] FROM [nt:base] WHERE [nt:base].[jcr:depth] = 3"); } @FixFor( "MODE-934" ) @Test public void shouldParseQueryWithQualifiedNodeScoreInCriteria() { query = parse("select [nt:base].[jcr:primaryType] FROM [nt:base] WHERE [nt:base].[jcr:score] <= 1.3"); } @Test public void shouldParseQuery() { query = parse("SELECT post.\"jcr:uuid\", post.\"text\", post.\"user\" FROM [fincayra.Post] AS post JOIN [fincayra.User] AS u ON post.\"user\"=u.\"jcr:uuid\""); System.out.println(query); // SELECT * ... assertThat(query.columns().size(), is(3)); assertThat(query.columns().get(0).selectorName(), is(selectorName("post"))); assertThat(query.columns().get(0).getColumnName(), is("jcr:uuid")); assertThat(query.columns().get(0).getPropertyName(), is("jcr:uuid")); assertThat(query.columns().get(1).selectorName(), is(selectorName("post"))); assertThat(query.columns().get(1).getColumnName(), is("text")); assertThat(query.columns().get(1).getPropertyName(), is("text")); assertThat(query.columns().get(2).selectorName(), is(selectorName("post"))); assertThat(query.columns().get(2).getColumnName(), is("user")); assertThat(query.columns().get(2).getPropertyName(), is("user")); // FROM ... Join join = isJoin(query.source()); assertThat(join.getLeft(), is((Source)namedSelector(selectorName("fincayra.Post"), selectorName("post")))); assertThat(join.getRight(), is((Source)namedSelector(selectorName("fincayra.User"), selectorName("u")))); assertThat(join.type(), is(JoinType.INNER)); EquiJoinCondition joinCondition = isEquiJoinCondition(join.getJoinCondition()); assertThat(joinCondition.getSelector1Name(), is("post")); assertThat(joinCondition.getSelector2Name(), is("u")); assertThat(joinCondition.getProperty1Name(), is("user")); assertThat(joinCondition.getProperty2Name(), is("jcr:uuid")); // WHERE ... assertThat(query.constraint(), is(nullValue())); } @Test @FixFor( "MODE-1594" ) public void shouldParseSetQuery() { parseSetQuery("SELECT nodes.col1, nodes.col2 FROM table1 AS nodes UNION SELECT edges.col3, edges.col4 FROM table2 AS edges"); parseSetQuery("SELECT nodes.col1, nodes.col2 FROM table1 AS nodes INTERSECT SELECT edges.col3, edges.col4 FROM table2 AS edges"); parseSetQuery("SELECT nodes.col1, nodes.col2 FROM table1 AS nodes EXCEPT SELECT edges.col3, edges.col4 FROM table2 AS edges"); } protected Join isJoin( Source source ) { assertThat(source, is(instanceOf(Join.class))); return (Join)source; } protected PropertyExistence isPropertyExistence( Constraint constraint ) { assertThat(constraint, is(instanceOf(PropertyExistence.class))); return (PropertyExistence)constraint; } protected Not isNot( Constraint constraint ) { assertThat(constraint, is(instanceOf(Not.class))); return (Not)constraint; } protected Comparison isComparison( Constraint constraint ) { assertThat(constraint, is(instanceOf(Comparison.class))); return (Comparison)constraint; } protected DescendantNode isDescendantNodeCriteria( Constraint constraint ) { assertThat(constraint, is(instanceOf(DescendantNode.class))); return (DescendantNode)constraint; } protected SameNodeJoinCondition isSameNodeJoinCondition( JoinCondition condition ) { assertThat(condition, is(instanceOf(SameNodeJoinCondition.class))); return (SameNodeJoinCondition)condition; } protected EquiJoinCondition isEquiJoinCondition( JoinCondition condition ) { assertThat(condition, is(instanceOf(EquiJoinCondition.class))); return (EquiJoinCondition)condition; } protected DescendantNodeJoinCondition isDescendantNodeJoinCondition( JoinCondition condition ) { assertThat(condition, is(instanceOf(DescendantNodeJoinCondition.class))); return (DescendantNodeJoinCondition)condition; } protected And isAnd( Constraint constraint ) { assertThat(constraint, is(instanceOf(And.class))); return (And)constraint; } protected Or isOr( Constraint constraint ) { assertThat(constraint, is(instanceOf(Or.class))); return (Or)constraint; } protected NodePath nodePath( SelectorName name ) { return new NodePath(name); } protected PropertyValue propertyValue( SelectorName selectorName, String propertyName ) { return new PropertyValue(selectorName, propertyName); } protected Literal literal( Object value ) { return new Literal(value); } protected NamedSelector namedSelector( SelectorName selectorName ) { return new NamedSelector(selectorName); } protected NamedSelector namedSelector( SelectorName selectorName, SelectorName alias ) { return new NamedSelector(selectorName, alias); } // ---------------------------------------------------------------------------------------------------------------- // removeBracketsAndQuotes // ---------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------- // Utility methods // ---------------------------------------------------------------------------------------------------------------- protected Query parse( String query ) { QueryCommand command = parseCommand(query); assertThat(command, is(instanceOf(Query.class))); return (Query)command; } protected SetQuery parseSetQuery( String query ) { QueryCommand command = parser.parseQuery(query, typeSystem); assertThat(command, is(instanceOf(SetQuery.class))); return (SetQuery)command; } protected QueryCommand parseCommand( String query ) { return parser.parseQuery(query, typeSystem); } protected SelectorName selectorName( String name ) { return new SelectorName(name); } protected Name name( String name ) { return (Name)typeSystem.getTypeFactory(PropertyType.NAME.getName()).create(name); } protected Path path( String path ) { return (Path)typeSystem.getTypeFactory(PropertyType.PATH.getName()).create(path); } protected TokenStream tokens( String content ) { return new TokenStream(content, new BasicSqlQueryParser.SqlTokenizer(false), false).start(); } protected static class MockJcrTypeSystem extends JcrTypeSystem { protected final JcrValueFactory valueFactory; protected final TypeSystem delegate; protected final ExecutionContext executionContext; protected MockJcrTypeSystem( JcrValueFactory valueFactory ) { this.executionContext = new ExecutionContext(); this.valueFactory = valueFactory; this.delegate = this.executionContext.getValueFactories().getTypeSystem(); } @Override public Set<String> getTypeNames() { return delegate.getTypeNames(); } @Override public TypeFactory<?> getTypeFactory( Object prototype ) { return delegate.getTypeFactory(prototype); } @Override public TypeFactory<?> getTypeFactory( String typeName ) { return delegate.getTypeFactory(typeName); } @Override public TypeFactory<String> getStringFactory() { return delegate.getStringFactory(); } @Override public TypeFactory<Reference> getReferenceFactory() { return delegate.getReferenceFactory(); } @Override public TypeFactory<Path> getPathFactory() { return delegate.getPathFactory(); } @Override public TypeFactory<Long> getLongFactory() { return delegate.getLongFactory(); } @Override public TypeFactory<Double> getDoubleFactory() { return delegate.getDoubleFactory(); } @Override public TypeFactory<Name> getNameFactory() { return delegate.getNameFactory(); } @Override public TypeFactory<NodeKey> getNodeKeyFactory() { return delegate.getNodeKeyFactory(); } @Override public String getDefaultType() { return delegate.getDefaultType(); } @Override public Comparator<Object> getDefaultComparator() { return delegate.getDefaultComparator(); } @Override public TypeFactory<BigDecimal> getDecimalFactory() { return delegate.getDecimalFactory(); } @Override public TypeFactory<?> getDateTimeFactory() { return delegate.getDateTimeFactory(); } @Override public String getCompatibleType( String type1, String type2 ) { return delegate.getCompatibleType(type1, type2); } @Override public TypeFactory<?> getCompatibleType( TypeFactory<?> type1, TypeFactory<?> type2 ) { return delegate.getCompatibleType(type1, type2); } @Override public TypeFactory<Boolean> getBooleanFactory() { return delegate.getBooleanFactory(); } @Override public TypeFactory<BinaryValue> getBinaryFactory() { return delegate.getBinaryFactory(); } @Override public String asString( Object value ) { return delegate.asString(value); } @Override public JcrValueFactory getValueFactory() { return valueFactory; } } }