Author: rhauch
Date: 2009-10-15 15:57:20 -0400 (Thu, 15 Oct 2009)
New Revision: 1294
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
Modified:
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java
trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
Log:
DNA-467 Added more query optimizer rules, and changed the way that the query plans are
handled so that access nodes always are in the shape of a valid query.
Modified:
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java
===================================================================
---
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -47,17 +47,17 @@
addProblem(new Problem(Problem.Status.ERROR, Problem.DEFAULT_CODE, message,
params, null, null, throwable));
}
- public void addError( I18n message,
- String resource,
+ public void addError( String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.ERROR, Problem.DEFAULT_CODE, message,
params, resource, location, null));
}
public void addError( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.ERROR, Problem.DEFAULT_CODE, message,
params, resource, location, throwable));
}
@@ -76,18 +76,18 @@
}
public void addError( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.ERROR, code, message, params, resource,
location, null));
}
public void addError( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.ERROR, code, message, params, resource,
location, throwable));
}
@@ -103,17 +103,17 @@
addProblem(new Problem(Problem.Status.WARNING, Problem.DEFAULT_CODE, message,
params, null, null, throwable));
}
- public void addWarning( I18n message,
- String resource,
+ public void addWarning( String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.WARNING, Problem.DEFAULT_CODE, message,
params, resource, location, null));
}
public void addWarning( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.WARNING, Problem.DEFAULT_CODE, message,
params, resource, location, throwable));
}
@@ -132,18 +132,18 @@
}
public void addWarning( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.WARNING, code, message, params, resource,
location, null));
}
public void addWarning( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.WARNING, code, message, params, resource,
location, throwable));
}
@@ -159,17 +159,17 @@
addProblem(new Problem(Problem.Status.INFO, Problem.DEFAULT_CODE, message,
params, null, null, throwable));
}
- public void addInfo( I18n message,
- String resource,
+ public void addInfo( String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.INFO, Problem.DEFAULT_CODE, message,
params, resource, location, null));
}
public void addInfo( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.INFO, Problem.DEFAULT_CODE, message,
params, resource, location, throwable));
}
@@ -188,18 +188,18 @@
}
public void addInfo( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.INFO, code, message, params, resource,
location, null));
}
public void addInfo( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
addProblem(new Problem(Problem.Status.INFO, code, message, params, resource,
location, throwable));
}
Modified:
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java
===================================================================
---
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -54,12 +54,12 @@
/**
* {@inheritDoc}
*
- * @see
org.jboss.dna.common.collection.Problems#addError(org.jboss.dna.common.i18n.I18n,
java.lang.String, java.lang.String,
+ * @see org.jboss.dna.common.collection.Problems#addError(java.lang.String,
java.lang.String, org.jboss.dna.common.i18n.I18n,
* java.lang.Object[])
*/
- public void addError( I18n message,
- String resource,
+ public void addError( String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -78,13 +78,12 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addError(int,
org.jboss.dna.common.i18n.I18n, java.lang.String,
- * java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addError(int, String, String, I18n,
Object...)
*/
public void addError( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -104,13 +103,13 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable,
org.jboss.dna.common.i18n.I18n,
- * java.lang.String, java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable,
java.lang.String, java.lang.String,
+ * org.jboss.dna.common.i18n.I18n,java.lang.Object[])
*/
public void addError( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -131,14 +130,14 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable, int,
org.jboss.dna.common.i18n.I18n,
- * java.lang.String, java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable, int,
java.lang.String, java.lang.String,
+ * org.jboss.dna.common.i18n.I18n, java.lang.Object[])
*/
public void addError( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -156,12 +155,12 @@
/**
* {@inheritDoc}
*
- * @see
org.jboss.dna.common.collection.Problems#addInfo(org.jboss.dna.common.i18n.I18n,
java.lang.String, java.lang.String,
+ * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.String,
java.lang.String, org.jboss.dna.common.i18n.I18n,
* java.lang.Object[])
*/
- public void addInfo( I18n message,
- String resource,
+ public void addInfo( String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -180,13 +179,13 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addInfo(int,
org.jboss.dna.common.i18n.I18n, java.lang.String,
- * java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addInfo(int, java.lang.String,
java.lang.String,
+ * org.jboss.dna.common.i18n.I18n, java.lang.Object[])
*/
public void addInfo( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -206,13 +205,13 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable,
org.jboss.dna.common.i18n.I18n,
- * java.lang.String, java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable,
java.lang.String, java.lang.String,
+ * org.jboss.dna.common.i18n.I18n, java.lang.Object[])
*/
public void addInfo( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -233,14 +232,14 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable, int,
org.jboss.dna.common.i18n.I18n,
- * java.lang.String, java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable, int,
java.lang.String, java.lang.String,
+ * org.jboss.dna.common.i18n.I18n, java.lang.Object[])
*/
public void addInfo( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -258,12 +257,12 @@
/**
* {@inheritDoc}
*
- * @see
org.jboss.dna.common.collection.Problems#addWarning(org.jboss.dna.common.i18n.I18n,
java.lang.String,
- * java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.String,
java.lang.String,
+ * org.jboss.dna.common.i18n.I18n, java.lang.Object[])
*/
- public void addWarning( I18n message,
- String resource,
+ public void addWarning( String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -282,13 +281,13 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addWarning(int,
org.jboss.dna.common.i18n.I18n, java.lang.String,
- * java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addWarning(int, java.lang.String,
java.lang.String,
+ * org.jboss.dna.common.i18n.I18n, java.lang.Object[])
*/
public void addWarning( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -308,13 +307,13 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable,
org.jboss.dna.common.i18n.I18n,
- * java.lang.String, java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable,
java.lang.String, java.lang.String ,
+ * org.jboss.dna.common.i18n.I18n,java.lang.Object[])
*/
public void addWarning( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
@@ -335,14 +334,14 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable, int,
org.jboss.dna.common.i18n.I18n,
- * java.lang.String, java.lang.String, java.lang.Object[])
+ * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable, int,
java.lang.String, java.lang.String,
+ * org.jboss.dna.common.i18n.I18n,java.lang.Object[])
*/
public void addWarning( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params ) {
throw new UnsupportedOperationException();
}
Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java
===================================================================
---
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -56,14 +56,14 @@
* Add an error message with a description of the resource, its location, and the
parameters that should be used when
* localizing the message
*
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
- void addError( I18n message,
- String resource,
+ void addError( String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -71,15 +71,15 @@
* when localizing the message
*
* @param throwable the exception that represents the error; may be null
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addError( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -111,15 +111,15 @@
* localizing the message
*
* @param code the error code
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addError( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -128,16 +128,16 @@
*
* @param throwable the exception that represents the error; may be null
* @param code the error code
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addError( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -164,14 +164,14 @@
* Add a warning message with a description of the resource, its location, and the
parameters that should be used when
* localizing the message
*
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
- void addWarning( I18n message,
- String resource,
+ void addWarning( String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -179,15 +179,15 @@
* used when localizing the message
*
* @param throwable the exception that represents the warning; may be null
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addWarning( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -219,15 +219,15 @@
* localizing the message
*
* @param code the problem code
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addWarning( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -236,16 +236,16 @@
*
* @param throwable the exception that represents the warning; may be null
* @param code the problem code
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addWarning( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -272,14 +272,14 @@
* Add an informational message with a description of the resource, its location, and
the parameters that should be used when
* localizing the message
*
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
- void addInfo( I18n message,
- String resource,
+ void addInfo( String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -287,15 +287,15 @@
* be used when localizing the message
*
* @param throwable the exception that represents the problem; may be null
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addInfo( Throwable throwable,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -327,15 +327,15 @@
* localizing the message
*
* @param code the problem code
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addInfo( int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
@@ -344,16 +344,16 @@
*
* @param throwable the exception that represents the problem; may be null
* @param code the problem code
- * @param message the internationalized message describing the problem
* @param resource the description of the resource; may be null
* @param location the location of the resource; may be null
+ * @param message the internationalized message describing the problem
* @param params the values for the parameters in the message
*/
void addInfo( Throwable throwable,
int code,
- I18n message,
String resource,
String location,
+ I18n message,
Object... params );
/**
Modified:
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java
===================================================================
---
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -23,6 +23,7 @@
*/
package org.jboss.dna.common.collection;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import net.jcip.annotations.NotThreadSafe;
@@ -57,4 +58,23 @@
protected List<Problem> getProblems() {
return this.problems != null ? problems : EMPTY_PROBLEMS;
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Iterator<Problem> iter = getProblems().iterator();
+ if (iter.hasNext()) {
+ sb.append(iter.next());
+ while (iter.hasNext()) {
+ sb.append("\n");
+ sb.append(iter.next());
+ }
+ }
+ return sb.toString();
+ }
}
Modified:
trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java
===================================================================
---
trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -104,7 +104,7 @@
@Test
public void
shouldAddErrorByMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
error = new Problem(Status.ERROR, Problem.DEFAULT_CODE, message, new Object[]
{"error msg"}, resource, location, null);
- problems.addError(error.getMessage(), error.getResource(), error.getLocation(),
error.getParameters());
+ problems.addError(error.getResource(), error.getLocation(), error.getMessage(),
error.getParameters());
assertThat(problems.iterator().next(), is(error));
assertThat(problems.isEmpty(), is(false));
assertThat(problems.size(), is(1));
@@ -113,7 +113,7 @@
@Test
public void shouldAddErrorByCodeAndMesssageAndResourceAndLocationAndParameters() {
error = new Problem(Status.ERROR, 1, message, new Object[] {"error
msg"}, resource, location, null);
- problems.addError(error.getCode(), error.getMessage(), error.getResource(),
error.getLocation(), error.getParameters());
+ problems.addError(error.getCode(), error.getResource(), error.getLocation(),
error.getMessage(), error.getParameters());
assertThat(problems.iterator().next(), is(error));
assertThat(problems.isEmpty(), is(false));
assertThat(problems.size(), is(1));
@@ -124,9 +124,9 @@
error = new Problem(Status.ERROR, Problem.DEFAULT_CODE, message, new Object[]
{"error msg"}, resource, location,
throwable);
problems.addError(error.getThrowable(),
- error.getMessage(),
error.getResource(),
error.getLocation(),
+ error.getMessage(),
error.getParameters());
assertThat(problems.iterator().next(), is(error));
assertThat(problems.isEmpty(), is(false));
@@ -138,9 +138,9 @@
error = new Problem(Status.ERROR, 1, message, new Object[] {"error
msg"}, resource, location, throwable);
problems.addError(error.getThrowable(),
error.getCode(),
- error.getMessage(),
error.getResource(),
error.getLocation(),
+ error.getMessage(),
error.getParameters());
assertThat(problems.iterator().next(), is(error));
assertThat(problems.isEmpty(), is(false));
@@ -186,7 +186,7 @@
public void
shouldAddWarningByMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
warning = new Problem(Status.WARNING, Problem.DEFAULT_CODE, message, new Object[]
{"warning msg"}, resource, location,
null);
- problems.addWarning(warning.getMessage(), warning.getResource(),
warning.getLocation(), warning.getParameters());
+ problems.addWarning(warning.getResource(), warning.getLocation(),
warning.getMessage(), warning.getParameters());
assertThat(problems.iterator().next(), is(warning));
assertThat(problems.isEmpty(), is(false));
assertThat(problems.size(), is(1));
@@ -196,9 +196,9 @@
public void shouldAddWarningByCodeAndMesssageAndResourceAndLocationAndParameters() {
warning = new Problem(Status.WARNING, 1, message, new Object[] {"warning
msg"}, resource, location, null);
problems.addWarning(warning.getCode(),
- warning.getMessage(),
warning.getResource(),
warning.getLocation(),
+ warning.getMessage(),
warning.getParameters());
assertThat(problems.iterator().next(), is(warning));
assertThat(problems.isEmpty(), is(false));
@@ -210,9 +210,9 @@
warning = new Problem(Status.WARNING, Problem.DEFAULT_CODE, message, new Object[]
{"warning msg"}, resource, location,
throwable);
problems.addWarning(warning.getThrowable(),
- warning.getMessage(),
warning.getResource(),
warning.getLocation(),
+ warning.getMessage(),
warning.getParameters());
assertThat(problems.iterator().next(), is(warning));
assertThat(problems.isEmpty(), is(false));
@@ -224,9 +224,9 @@
warning = new Problem(Status.WARNING, 1, message, new Object[] {"warning
msg"}, resource, location, throwable);
problems.addWarning(warning.getThrowable(),
warning.getCode(),
- warning.getMessage(),
warning.getResource(),
warning.getLocation(),
+ warning.getMessage(),
warning.getParameters());
assertThat(problems.iterator().next(), is(warning));
assertThat(problems.isEmpty(), is(false));
@@ -271,7 +271,7 @@
@Test
public void
shouldAddInfoByMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
info = new Problem(Status.INFO, Problem.DEFAULT_CODE, message, new Object[]
{"info msg"}, resource, location, null);
- problems.addInfo(info.getMessage(), info.getResource(), info.getLocation(),
info.getParameters());
+ problems.addInfo(info.getResource(), info.getLocation(), info.getMessage(),
info.getParameters());
assertThat(problems.iterator().next(), is(info));
assertThat(problems.isEmpty(), is(false));
assertThat(problems.size(), is(1));
@@ -280,7 +280,7 @@
@Test
public void shouldAddInfoByCodeAndMesssageAndResourceAndLocationAndParameters() {
info = new Problem(Status.INFO, 1, message, new Object[] {"info msg"},
resource, location, null);
- problems.addInfo(info.getCode(), info.getMessage(), info.getResource(),
info.getLocation(), info.getParameters());
+ problems.addInfo(info.getCode(), info.getResource(), info.getLocation(),
info.getMessage(), info.getParameters());
assertThat(problems.iterator().next(), is(info));
assertThat(problems.isEmpty(), is(false));
assertThat(problems.size(), is(1));
@@ -289,7 +289,7 @@
@Test
public void
shouldAddInfoByThrowableAndMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
info = new Problem(Status.INFO, Problem.DEFAULT_CODE, message, new Object[]
{"info msg"}, resource, location, throwable);
- problems.addInfo(info.getThrowable(), info.getMessage(), info.getResource(),
info.getLocation(), info.getParameters());
+ problems.addInfo(info.getThrowable(), info.getResource(), info.getLocation(),
info.getMessage(), info.getParameters());
assertThat(problems.iterator().next(), is(info));
assertThat(problems.isEmpty(), is(false));
assertThat(problems.size(), is(1));
@@ -300,9 +300,9 @@
info = new Problem(Status.INFO, 1, message, new Object[] {"info msg"},
resource, location, throwable);
problems.addInfo(info.getThrowable(),
info.getCode(),
- info.getMessage(),
info.getResource(),
info.getLocation(),
+ info.getMessage(),
info.getParameters());
assertThat(problems.iterator().next(), is(info));
assertThat(problems.isEmpty(), is(false));
@@ -318,9 +318,9 @@
assertThat(problems.size(), is(0));
problems.addWarning(warning.getThrowable(),
warning.getCode(),
- warning.getMessage(),
warning.getResource(),
warning.getLocation(),
+ warning.getMessage(),
warning.getParameters());
assertThat(problems.hasErrors(), is(false));
assertThat(problems.hasWarnings(), is(true));
@@ -329,9 +329,9 @@
assertThat(problems.size(), is(1));
problems.addError(error.getThrowable(),
error.getCode(),
- error.getMessage(),
error.getResource(),
error.getLocation(),
+ error.getMessage(),
error.getParameters());
assertThat(problems.hasErrors(), is(true));
assertThat(problems.hasWarnings(), is(true));
@@ -340,9 +340,9 @@
assertThat(problems.size(), is(2));
problems.addInfo(info.getThrowable(),
info.getCode(),
- info.getMessage(),
info.getResource(),
info.getLocation(),
+ info.getMessage(),
info.getParameters());
assertThat(problems.hasErrors(), is(true));
assertThat(problems.hasWarnings(), is(true));
@@ -351,27 +351,27 @@
assertThat(problems.size(), is(3));
problems.addWarning(warning.getThrowable(),
warning.getCode(),
- warning.getMessage(),
warning.getResource(),
warning.getLocation(),
+ warning.getMessage(),
warning.getParameters());
problems.addError(error.getThrowable(),
error.getCode(),
- error.getMessage(),
error.getResource(),
error.getLocation(),
+ error.getMessage(),
error.getParameters());
problems.addWarning(warning.getThrowable(),
warning.getCode(),
- warning.getMessage(),
warning.getResource(),
warning.getLocation(),
+ warning.getMessage(),
warning.getParameters());
problems.addError(error.getThrowable(),
error.getCode(),
- error.getMessage(),
error.getResource(),
error.getLocation(),
+ error.getMessage(),
error.getParameters());
assertThat(problems.hasErrors(), is(true));
assertThat(problems.hasWarnings(), is(true));
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java 2009-10-15
19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -23,6 +23,7 @@
*/
package org.jboss.dna.graph.query.model;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
@@ -32,6 +33,7 @@
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.ValueFactory;
/**
* A set of common visitors that can be reused or extended, and methods that provide easy
construction and calling of visitors.
@@ -51,7 +53,7 @@
*/
public static <StrategyVisitor extends Visitor> StrategyVisitor visitAll(
Visitable visitable,
StrategyVisitor strategyVisitor ) {
- visitable.accept(new WalkAllVisitor(strategyVisitor));
+ if (visitable != null) visitable.accept(new WalkAllVisitor(strategyVisitor));
return strategyVisitor;
}
@@ -66,7 +68,7 @@
*/
public static <GeneralVisitor extends Visitor> GeneralVisitor visit( Visitable
visitable,
GeneralVisitor
visitor ) {
- visitable.accept(visitor);
+ if (visitable != null) visitable.accept(visitor);
return visitor;
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -32,8 +32,8 @@
import org.jboss.dna.graph.query.plan.PlanNode.Type;
/**
- * An {@link OptimizerRule optimizer rule} that inserts an ACCESS above each SOURCE node
in a query plan. This rule is often the
- * first rule to run against a {@link CanonicalPlanner canonical plan} (see
+ * An {@link OptimizerRule optimizer rule} that inserts an ACCESS above each SOURCE leaf
node in a query plan. This rule is often
+ * the first rule to run against a {@link CanonicalPlanner canonical plan} (see
* {@link RuleBasedOptimizer#populateRuleStack(LinkedList, PlanHints)}.
* <p>
* Before:
@@ -72,7 +72,7 @@
LinkedList<OptimizerRule> ruleStack ) {
// On each of the source nodes ...
for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
- // The source node should have no children ...
+ // The source node may have children if it is a view ...
if (source.getChildCount() != 0) continue;
// Create the ACCESS node, set the selectors, and insert above the source
node ...
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -0,0 +1,192 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitable;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.model.Visitors.AbstractVisitor;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that copies SELECT nodes that apply to one
side of a equi-join condition so that they
+ * also apply to the other side fo the equi-join condition.
+ */
+@Immutable
+public class CopyCriteria implements OptimizerRule {
+
+ public static final CopyCriteria INSTANCE = new CopyCriteria();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+ * org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+ */
+ public PlanNode execute( QueryContext context,
+ PlanNode plan,
+ LinkedList<OptimizerRule> ruleStack ) {
+ Set<PlanNode> copiedSelectNodes = new HashSet<PlanNode>();
+
+ for (PlanNode join : plan.findAllAtOrBelow(Type.JOIN)) {
+ // Get the join condition ...
+ JoinCondition joinCondition = join.getProperty(Property.JOIN_CONDITION,
JoinCondition.class);
+ if (joinCondition instanceof EquiJoinCondition) {
+ EquiJoinCondition equiJoinCondition = (EquiJoinCondition)joinCondition;
+ SelectorName selector1 = equiJoinCondition.getSelector1Name();
+ SelectorName selector2 = equiJoinCondition.getSelector2Name();
+ Name property1 = equiJoinCondition.getProperty1Name();
+ Name property2 = equiJoinCondition.getProperty2Name();
+
+ // Walk up the tree looking for SELECT nodes that apply to one of the
sides ...
+ PlanNode node = join.getParent();
+ while (node != null) {
+ if (!copiedSelectNodes.contains(node)) {
+ PlanNode copy = copySelectNode(context, node, selector1,
property1, selector2, property2);
+ if (copy != null) {
+ node.insertAsParent(copy);
+ copiedSelectNodes.add(node);
+ copiedSelectNodes.add(copy);
+ } else {
+ copy = copySelectNode(context, node, selector2, property2,
selector1, property1);
+ if (copy != null) {
+ node.insertAsParent(copy);
+ copiedSelectNodes.add(node);
+ copiedSelectNodes.add(copy);
+ }
+ }
+ }
+ node = node.getParent();
+ }
+ }
+ }
+ return plan;
+ }
+
+ protected PlanNode copySelectNode( QueryContext context,
+ PlanNode selectNode,
+ SelectorName selectorName,
+ Name propertyName,
+ SelectorName copySelectorName,
+ Name copyPropertyName ) {
+ if (selectNode.isNot(Type.SELECT)) return null;
+ if (selectNode.getSelectors().size() != 1 ||
!selectNode.getSelectors().contains(selectorName)) return null;
+
+ Constraint constraint = selectNode.getProperty(Property.SELECT_CRITERIA,
Constraint.class);
+ Set<Column> columns = getColumnsReferencedBy(constraint,
context.getExecutionContext());
+ if (columns.size() != 1) return null;
+ Column column = columns.iterator().next();
+ if (!column.getSelectorName().equals(selectorName)) return null;
+ if (!column.getPropertyName().equals(propertyName)) return null;
+
+ // We know that this constraint ONLY applies to the referenced selector and
property,
+ // so we will duplicate this constraint ...
+
+ // Create the new node ...
+ PlanNode copy = new PlanNode(Type.SELECT, copySelectorName);
+
+ // Copy the constraint, but change the references to the copy selector and
property ...
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
+ PlanUtil.ColumnMapping mappings = new PlanUtil.ColumnMapping(selectorName);
+ mappings.map(stringFactory.create(propertyName), new Column(copySelectorName,
copyPropertyName,
+
stringFactory.create(copyPropertyName)));
+ Constraint newCriteria = PlanUtil.replaceReferences(context, constraint,
mappings, copy);
+ copy.setProperty(Property.SELECT_CRITERIA, newCriteria);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * Get the set of Column objects that represent those columns referenced by the
visitable object.
+ *
+ * @param visitable the object to be visited
+ * @param context the context; may not be null
+ * @return the set of Column objects, with column names that always are the
string-form of the
+ * {@link Column#getPropertyName() property name}; never null
+ */
+ public static Set<Column> getColumnsReferencedBy( Visitable visitable,
+ ExecutionContext context ) {
+ if (visitable == null) return Collections.emptySet();
+ final ValueFactory<String> stringFactory =
context.getValueFactories().getStringFactory();
+ final Set<Column> symbols = new HashSet<Column>();
+ // Walk the entire structure, so only supply a StrategyVisitor (that does no
navigation) ...
+ Visitors.visitAll(visitable, new AbstractVisitor() {
+ protected void addColumnFor( SelectorName selectorName,
+ Name property ) {
+ symbols.add(new Column(selectorName, property,
stringFactory.create(property)));
+ }
+
+ @Override
+ public void visit( Column column ) {
+ symbols.add(column);
+ }
+
+ @Override
+ public void visit( EquiJoinCondition joinCondition ) {
+ addColumnFor(joinCondition.getSelector1Name(),
joinCondition.getProperty1Name());
+ addColumnFor(joinCondition.getSelector2Name(),
joinCondition.getProperty2Name());
+ }
+
+ @Override
+ public void visit( PropertyExistence prop ) {
+ addColumnFor(prop.getSelectorName(), prop.getPropertyName());
+ }
+
+ @Override
+ public void visit( PropertyValue prop ) {
+ addColumnFor(prop.getSelectorName(), prop.getPropertyName());
+ }
+ });
+ return symbols;
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -0,0 +1,115 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * This rule attempts to ensure the proper location of {@link Type#PROJECT} nodes. For
example, every {@link Type#ACCESS} node
+ * needs to have a PROJECT node below it and above any other nodes (e.g., {@link
Type#SELECT} or {@link Type#SOURCE} nodes). This
+ * rule ensures that the PROJECT exists, but it also attempts to reduce any unnecessary
columns in existing PROJECT nodes.
+ */
+public class PushProjects implements OptimizerRule {
+
+ public static final PushProjects INSTANCE = new PushProjects();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+ * org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+ */
+ public PlanNode execute( QueryContext context,
+ PlanNode plan,
+ LinkedList<OptimizerRule> ruleStack ) {
+
+ // Find all of the ACCESS nodes to make sure there is a PROJECT ...
+ for (PlanNode access : plan.findAllAtOrBelow(Type.ACCESS)) {
+ // Find the first node that is below any LIMIT, SORT, or DUP_REMOVE ...
+ PlanNode parentOfProject = access;
+ while (parentOfProject.isOneOf(Type.LIMIT, Type.SORT, Type.DUP_REMOVE)) {
+ parentOfProject = parentOfProject.getParent();
+ }
+
+ // Is there already a PROJECT here ?
+ assert parentOfProject.getChildCount() == 1; // should only have one child
...
+ PlanNode child = parentOfProject.getFirstChild(); // should only have one
child ...
+ if (child.is(Type.PROJECT)) continue;
+
+ // If the parent of the ACCESS node is a PROJECT, then we can simply move it
to here ...
+ PlanNode accessParent = access.getParent();
+ if (accessParent != null && accessParent.is(Type.PROJECT)) {
+ PlanNode project = accessParent;
+ if (project == plan) {
+ // The PROJECT node is the root, so the ACCESS node will be the root
...
+ plan = access;
+ access.removeFromParent();
+ } else {
+ project.extractFromParent();
+ }
+ child.insertAsParent(project);
+ if (plan == access) return plan;
+ continue;
+ }
+
+ // There is no PROJECT, so find the columns that are required by the plan
above this point ...
+ List<Column> requiredColumns = PlanUtil.findRequiredColumns(context,
child);
+
+ // And insert the PROJECT ...
+ PlanNode projectNode = new PlanNode(Type.PROJECT);
+ projectNode.setProperty(Property.PROJECT_COLUMNS, requiredColumns);
+ projectNode.addSelectors(getSelectorsFor(requiredColumns));
+ child.insertAsParent(projectNode);
+ }
+ return plan;
+ }
+
+ protected Set<SelectorName> getSelectorsFor( List<Column> columns ) {
+ Set<SelectorName> selectors = new HashSet<SelectorName>();
+ for (Column column : columns) {
+ selectors.add(column.getSelectorName());
+ }
+ return selectors;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -64,10 +64,10 @@
* <pre>
* ...
* |
+ * ACCESS
+ * |
* PROJECT with the list of columns being SELECTed
* |
- * ACCESS
- * |
* SELECT1
* | One or more SELECT plan nodes that each have
* SELECT2 a single non-join constraint that are then all AND-ed
@@ -104,6 +104,9 @@
* ACCESS ACCESS
* ('s1') ('s2')
* | |
+ * PROJECT PROJECT
+ * ('s1') ('s2')
+ * | |
* SOURCE SOURCE
* ('s1') ('s2')
* </pre>
@@ -123,6 +126,9 @@
* ACCESS ACCESS
* ('s1') ('s2')
* | |
+ * PROJECT PROJECT
+ * ('s1') ('s2')
+ * | |
* SELECT1 SELECT2
* ('s1') ('s2')
* | |
@@ -134,6 +140,9 @@
* </pre>
*
* </p>
+ * <p>
+ * Also, any SELECT that applies to one side of an equi-join will be applied to
<i>both</i> sides of the JOIN.
+ * </p>
*/
@Immutable
public class PushSelectCriteria implements OptimizerRule {
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -0,0 +1,132 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.query.validate.Schemata.Table;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that replaces any SOURCE nodes that happen to
be {@link View views}. This rewriting
+ * changes all of the elements of the plan that reference the SOURCE and it's
columns, including criteria, project nodes, etc.
+ */
+@Immutable
+public class ReplaceViews implements OptimizerRule {
+
+ public static final ReplaceViews INSTANCE = new ReplaceViews();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+ * org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+ */
+ public PlanNode execute( QueryContext context,
+ PlanNode plan,
+ LinkedList<OptimizerRule> ruleStack ) {
+ // Prepare a cache of the view plans ...
+ Map<SelectorName, PlanNode> viewPlanCache = new HashMap<SelectorName,
PlanNode>();
+ CanonicalPlanner planner = new CanonicalPlanner();
+
+ // Prepare the maps that will record the old-to-new mappings from the old view
SOURCE nodes to the table SOURCEs ...
+
+ // For each of the SOURCE nodes ...
+ Schemata schemata = context.getSchemata();
+ Set<PlanNode> processedSources = new HashSet<PlanNode>();
+ boolean foundViews = false;
+ do {
+ foundViews = false;
+ for (PlanNode sourceNode : plan.findAllAtOrBelow(Type.SOURCE)) {
+ if (processedSources.contains(sourceNode)) continue;
+ processedSources.add(sourceNode);
+
+ // Resolve the node to find the definition in the schemata ...
+ SelectorName tableName = sourceNode.getProperty(Property.SOURCE_NAME,
SelectorName.class);
+ Table table = schemata.getTable(tableName);
+ if (table instanceof View) {
+ View view = (View)table;
+ PlanNode viewPlan = viewPlanCache.get(tableName);
+ if (viewPlan == null) {
+ viewPlan = planner.createPlan(context, view.getDefinition());
+ if (viewPlan != null) viewPlanCache.put(tableName, viewPlan);
+ }
+ if (viewPlan == null) continue; // there were likely errors when
creating the plan
+ viewPlan = viewPlan.clone();
+
+ // Insert the view plan under the parent SOURCE node ...
+ sourceNode.addLastChild(viewPlan);
+
+ // Update the plan above this node to replace references to the view
columns with references to the
+ // tables/views used by the view ...
+ PlanUtil.ColumnMapping viewMappings = PlanUtil.createMappingFor(view,
viewPlan);
+ PlanUtil.replaceViewReferences(context, viewPlan, viewMappings);
+
+ if (viewPlan.is(Type.PROJECT)) {
+ // The PROJECT from the plan may actually not be needed if there
is another PROJECT above it ...
+ PlanNode node = viewPlan.getParent();
+ while (node != null) {
+ if (node.is(Type.PROJECT) &&
viewPlan.getSelectors().containsAll(node.getSelectors())) {
+ viewPlan.extractFromParent();
+ break;
+ }
+ node = node.getParent();
+ }
+ }
+ foundViews = true;
+ }
+ }
+ } while (foundViews);
+
+ if (foundViews) {
+ // We re-wrote at least one SOURCE, but the resulting plan tree for the view
could actually reference
+ // other views. Therefore, re-run this rule ...
+ ruleStack.addFirst(this);
+ }
+ return plan;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -24,46 +24,17 @@
package org.jboss.dna.graph.query.optimize;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
import net.jcip.annotations.Immutable;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.model.And;
-import org.jboss.dna.graph.query.model.ChildNode;
-import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
-import org.jboss.dna.graph.query.model.Column;
-import org.jboss.dna.graph.query.model.Comparison;
-import org.jboss.dna.graph.query.model.Constraint;
-import org.jboss.dna.graph.query.model.DescendantNode;
-import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
-import org.jboss.dna.graph.query.model.DynamicOperand;
import org.jboss.dna.graph.query.model.EquiJoinCondition;
-import org.jboss.dna.graph.query.model.FullTextSearch;
-import org.jboss.dna.graph.query.model.FullTextSearchScore;
import org.jboss.dna.graph.query.model.JoinCondition;
-import org.jboss.dna.graph.query.model.Length;
-import org.jboss.dna.graph.query.model.LowerCase;
-import org.jboss.dna.graph.query.model.NodeDepth;
-import org.jboss.dna.graph.query.model.NodeLocalName;
-import org.jboss.dna.graph.query.model.NodeName;
-import org.jboss.dna.graph.query.model.NodePath;
-import org.jboss.dna.graph.query.model.Not;
-import org.jboss.dna.graph.query.model.Or;
-import org.jboss.dna.graph.query.model.Ordering;
-import org.jboss.dna.graph.query.model.PropertyExistence;
-import org.jboss.dna.graph.query.model.PropertyValue;
-import org.jboss.dna.graph.query.model.SameNode;
-import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.model.StaticOperand;
-import org.jboss.dna.graph.query.model.UpperCase;
import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
import org.jboss.dna.graph.query.plan.PlanNode.Property;
import org.jboss.dna.graph.query.plan.PlanNode.Type;
import org.jboss.dna.graph.query.validate.Schemata;
@@ -170,7 +141,7 @@
ruleStack.addFirst(this);
// Now rewrite the various portions of the plan that make use of the
now-removed selectors ...
- replaceReferencesToRemovedSource(context, plan, rewrittenSelectors);
+ PlanUtil.replaceReferencesToRemovedSource(context, plan,
rewrittenSelectors);
} else {
// There are no-untouched JOIN nodes, which means the sole JOIN node was
rewritten as a single SOURCE node
assert plan.findAllAtOrBelow(Type.JOIN).isEmpty();
@@ -206,255 +177,6 @@
}
}
- protected void replaceReferencesToRemovedSource( QueryContext context,
- PlanNode planNode,
- Map<SelectorName,
SelectorName> rewrittenSelectors ) {
- switch (planNode.getType()) {
- case PROJECT:
- List<Column> columns =
planNode.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
- for (int i = 0; i != columns.size(); ++i) {
- Column column = columns.get(i);
- SelectorName replacement =
rewrittenSelectors.get(column.getSelectorName());
- if (replacement != null) {
- columns.set(i, new Column(replacement, column.getPropertyName(),
column.getColumnName()));
- }
- }
- break;
- case SELECT:
- Constraint constraint = planNode.getProperty(Property.SELECT_CRITERIA,
Constraint.class);
- Constraint newConstraint = replaceReferencesToRemovedSource(context,
constraint, rewrittenSelectors);
- if (constraint != newConstraint) {
- planNode.setProperty(Property.SELECT_CRITERIA, newConstraint);
- }
- break;
- case SORT:
- List<Object> orderBys =
planNode.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
- if (orderBys != null && !orderBys.isEmpty()) {
- if (orderBys.get(0) instanceof SelectorName) {
- for (int i = 0; i != orderBys.size(); ++i) {
- SelectorName selectorName = (SelectorName)orderBys.get(i);
- SelectorName replacement =
rewrittenSelectors.get(selectorName);
- if (replacement != null) {
- orderBys.set(i, replacement);
- }
- }
- } else {
- for (int i = 0; i != orderBys.size(); ++i) {
- Ordering ordering = (Ordering)orderBys.get(i);
- DynamicOperand operand = ordering.getOperand();
- orderBys.set(i, replaceReferencesToRemovedSource(context,
operand, rewrittenSelectors));
- }
- }
- }
- break;
- case JOIN:
- // Update the join condition ...
- JoinCondition joinCondition =
planNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
- JoinCondition newCondition = replaceReferencesToRemovedSource(context,
joinCondition, rewrittenSelectors);
- if (joinCondition != newCondition) {
- planNode.setProperty(Property.JOIN_CONDITION, newCondition);
- }
-
- // Update the join criteria (if there are some) ...
- List<Constraint> constraints =
planNode.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
- if (constraints != null && !constraints.isEmpty()) {
- for (int i = 0; i != constraints.size(); ++i) {
- Constraint old = constraints.get(i);
- Constraint replacement =
replaceReferencesToRemovedSource(context, old, rewrittenSelectors);
- if (replacement != old) {
- constraints.set(i, replacement);
- }
- }
- }
- break;
- case GROUP:
- case SET_OPERATION:
- case DUP_REMOVE:
- case LIMIT:
- case NULL:
- case SOURCE:
- case ACCESS:
- // None of these have to be changed ...
- break;
- }
-
- // Update the selectors referenced by the node ...
- Set<SelectorName> selectorsToAdd = null;
- for (Iterator<SelectorName> iter = planNode.getSelectors().iterator();
iter.hasNext();) {
- SelectorName replacement = rewrittenSelectors.get(iter.next());
- if (replacement != null) {
- iter.remove();
- if (selectorsToAdd == null) selectorsToAdd = new
HashSet<SelectorName>();
- selectorsToAdd.add(replacement);
- }
- }
- if (selectorsToAdd != null) planNode.getSelectors().addAll(selectorsToAdd);
-
- // Now call recursively on the children ...
- for (PlanNode child : planNode) {
- replaceReferencesToRemovedSource(context, child, rewrittenSelectors);
- }
- }
-
- protected DynamicOperand replaceReferencesToRemovedSource( QueryContext context,
- DynamicOperand operand,
- Map<SelectorName,
SelectorName> rewrittenSelectors ) {
- if (operand instanceof FullTextSearchScore) {
- FullTextSearchScore score = (FullTextSearchScore)operand;
- SelectorName replacement = rewrittenSelectors.get(score.getSelectorName());
- if (replacement == null) return score;
- return new FullTextSearchScore(replacement);
- }
- if (operand instanceof Length) {
- Length operation = (Length)operand;
- PropertyValue wrapped = operation.getPropertyValue();
- SelectorName replacement =
rewrittenSelectors.get(wrapped.getSelectorName());
- if (replacement == null) return operand;
- return new Length(new PropertyValue(replacement,
wrapped.getPropertyName()));
- }
- if (operand instanceof LowerCase) {
- LowerCase operation = (LowerCase)operand;
- SelectorName replacement =
rewrittenSelectors.get(operation.getSelectorName());
- if (replacement == null) return operand;
- return new LowerCase(replaceReferencesToRemovedSource(context, operand,
rewrittenSelectors));
- }
- if (operand instanceof UpperCase) {
- UpperCase operation = (UpperCase)operand;
- SelectorName replacement =
rewrittenSelectors.get(operation.getSelectorName());
- if (replacement == null) return operand;
- return new UpperCase(replaceReferencesToRemovedSource(context, operand,
rewrittenSelectors));
- }
- if (operand instanceof NodeName) {
- NodeName name = (NodeName)operand;
- SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
- if (replacement == null) return name;
- return new NodeName(replacement);
- }
- if (operand instanceof NodeLocalName) {
- NodeLocalName name = (NodeLocalName)operand;
- SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
- if (replacement == null) return name;
- return new NodeLocalName(replacement);
- }
- if (operand instanceof PropertyValue) {
- PropertyValue value = (PropertyValue)operand;
- SelectorName replacement = rewrittenSelectors.get(value.getSelectorName());
- if (replacement == null) return operand;
- return new PropertyValue(replacement, value.getPropertyName());
- }
- if (operand instanceof NodeDepth) {
- NodeDepth depth = (NodeDepth)operand;
- SelectorName replacement = rewrittenSelectors.get(depth.getSelectorName());
- if (replacement == null) return operand;
- return new NodeDepth(replacement);
- }
- if (operand instanceof NodePath) {
- NodePath path = (NodePath)operand;
- SelectorName replacement = rewrittenSelectors.get(path.getSelectorName());
- if (replacement == null) return operand;
- return new NodePath(replacement);
- }
- return operand;
- }
-
- protected Constraint replaceReferencesToRemovedSource( QueryContext context,
- Constraint constraint,
- Map<SelectorName,
SelectorName> rewrittenSelectors ) {
- if (constraint instanceof And) {
- And and = (And)constraint;
- Constraint left = replaceReferencesToRemovedSource(context, and.getLeft(),
rewrittenSelectors);
- Constraint right = replaceReferencesToRemovedSource(context, and.getRight(),
rewrittenSelectors);
- if (left == and.getLeft() && right == and.getRight()) return and;
- return new And(left, right);
- }
- if (constraint instanceof Or) {
- Or or = (Or)constraint;
- Constraint left = replaceReferencesToRemovedSource(context, or.getLeft(),
rewrittenSelectors);
- Constraint right = replaceReferencesToRemovedSource(context, or.getRight(),
rewrittenSelectors);
- if (left == or.getLeft() && right == or.getRight()) return or;
- return new Or(left, right);
- }
- if (constraint instanceof Not) {
- Not not = (Not)constraint;
- Constraint wrapped = replaceReferencesToRemovedSource(context,
not.getConstraint(), rewrittenSelectors);
- if (wrapped == not.getConstraint()) return not;
- return new Not(wrapped);
- }
- if (constraint instanceof SameNode) {
- SameNode sameNode = (SameNode)constraint;
- SelectorName replacement =
rewrittenSelectors.get(sameNode.getSelectorName());
- if (replacement == null) return sameNode;
- return new SameNode(replacement, sameNode.getPath());
- }
- if (constraint instanceof ChildNode) {
- ChildNode childNode = (ChildNode)constraint;
- SelectorName replacement =
rewrittenSelectors.get(childNode.getSelectorName());
- if (replacement == null) return childNode;
- return new ChildNode(replacement, childNode.getParentPath());
- }
- if (constraint instanceof DescendantNode) {
- DescendantNode descendantNode = (DescendantNode)constraint;
- SelectorName replacement =
rewrittenSelectors.get(descendantNode.getSelectorName());
- if (replacement == null) return descendantNode;
- return new DescendantNode(replacement, descendantNode.getAncestorPath());
- }
- if (constraint instanceof PropertyExistence) {
- PropertyExistence existence = (PropertyExistence)constraint;
- SelectorName replacement =
rewrittenSelectors.get(existence.getSelectorName());
- if (replacement == null) return existence;
- return new PropertyExistence(replacement, existence.getPropertyName());
- }
- if (constraint instanceof FullTextSearch) {
- FullTextSearch search = (FullTextSearch)constraint;
- SelectorName replacement = rewrittenSelectors.get(search.getSelectorName());
- if (replacement == null) return search;
- return new PropertyExistence(replacement, search.getPropertyName());
- }
- if (constraint instanceof Comparison) {
- Comparison comparison = (Comparison)constraint;
- DynamicOperand lhs = comparison.getOperand1();
- StaticOperand rhs = comparison.getOperand2(); // Current only a literal;
therefore, no reference to selector
- DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs,
rewrittenSelectors);
- if (lhs == newLhs) return comparison;
- return new Comparison(newLhs, comparison.getOperator(), rhs);
- }
- return constraint;
- }
-
- protected JoinCondition replaceReferencesToRemovedSource( QueryContext context,
- JoinCondition
joinCondition,
- Map<SelectorName,
SelectorName> rewrittenSelectors ) {
- if (joinCondition instanceof EquiJoinCondition) {
- EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
- SelectorName replacement1 =
rewrittenSelectors.get(condition.getSelector1Name());
- SelectorName replacement2 =
rewrittenSelectors.get(condition.getSelector2Name());
- if (replacement1 == condition.getSelector1Name() && replacement2 ==
condition.getSelector2Name()) return condition;
- return new EquiJoinCondition(replacement1, condition.getProperty1Name(),
replacement2, condition.getProperty2Name());
- }
- if (joinCondition instanceof SameNodeJoinCondition) {
- SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
- SelectorName replacement1 =
rewrittenSelectors.get(condition.getSelector1Name());
- SelectorName replacement2 =
rewrittenSelectors.get(condition.getSelector2Name());
- if (replacement1 == condition.getSelector1Name() && replacement2 ==
condition.getSelector2Name()) return condition;
- return new SameNodeJoinCondition(replacement1, replacement2,
condition.getSelector2Path());
- }
- if (joinCondition instanceof ChildNodeJoinCondition) {
- ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
- SelectorName childSelector =
rewrittenSelectors.get(condition.getChildSelectorName());
- SelectorName parentSelector =
rewrittenSelectors.get(condition.getParentSelectorName());
- if (childSelector == condition.getChildSelectorName() &&
parentSelector == condition.getParentSelectorName()) return condition;
- return new ChildNodeJoinCondition(parentSelector, childSelector);
- }
- if (joinCondition instanceof DescendantNodeJoinCondition) {
- DescendantNodeJoinCondition condition =
(DescendantNodeJoinCondition)joinCondition;
- SelectorName ancestor =
rewrittenSelectors.get(condition.getAncestorSelectorName());
- SelectorName descendant =
rewrittenSelectors.get(condition.getDescendantSelectorName());
- if (ancestor == condition.getAncestorSelectorName() && descendant ==
condition.getDescendantSelectorName()) return condition;
- return new ChildNodeJoinCondition(ancestor, descendant);
- }
- return joinCondition;
- }
-
/**
* {@inheritDoc}
*
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -72,10 +72,15 @@
if (hints.hasJoin) {
ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
}
+ ruleStack.addFirst(PushProjects.INSTANCE);
if (hints.hasCriteria) {
ruleStack.addFirst(PushSelectCriteria.INSTANCE);
}
ruleStack.addFirst(AddAccessNodes.INSTANCE);
ruleStack.addFirst(RightOuterToLeftOuterJoins.INSTANCE);
+ ruleStack.addFirst(CopyCriteria.INSTANCE);
+ if (hints.hasView) {
+ ruleStack.addFirst(ReplaceViews.INSTANCE);
+ }
}
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -26,7 +26,7 @@
import org.jboss.dna.common.util.CheckArg;
/**
- * An exception signalling that an XPath query is invalid (but typically well-formed)
+ * An exception signalling that a query is invalid (but typically well-formed)
*/
public class InvalidQueryException extends RuntimeException {
@@ -34,38 +34,38 @@
*/
private static final long serialVersionUID = 1L;
- private final String xpath;
+ private final String query;
/**
* Create an exception with the invalid query.
*
- * @param xpath the XPath query that is invalid
+ * @param query the query that is invalid
*/
- public InvalidQueryException( String xpath ) {
+ public InvalidQueryException( String query ) {
super();
- this.xpath = xpath;
+ this.query = query;
}
/**
* Create an exception with the invalid query and a message.
*
- * @param xpath the XPath query that is invalid
+ * @param query the query that is invalid
* @param message
*/
- public InvalidQueryException( String xpath,
+ public InvalidQueryException( String query,
String message ) {
super(message);
- CheckArg.isNotNull(xpath, "xpath");
- this.xpath = xpath;
+ CheckArg.isNotNull(query, "query");
+ this.query = query;
}
/**
* Get the XPath query that is invalid.
*
- * @return the query
+ * @return the query; never null
*/
- public String getXPath() {
- return xpath;
+ public String getQuery() {
+ return query;
}
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -31,6 +31,7 @@
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.query.QueryContext;
import org.jboss.dna.graph.query.model.AllNodes;
import org.jboss.dna.graph.query.model.And;
@@ -53,6 +54,7 @@
import org.jboss.dna.graph.query.plan.PlanNode.Type;
import org.jboss.dna.graph.query.validate.Schemata;
import org.jboss.dna.graph.query.validate.Schemata.Table;
+import org.jboss.dna.graph.query.validate.Schemata.View;
/**
* The planner that produces a canonical query plan given a {@link QueryCommand query
command}.
@@ -85,7 +87,7 @@
* from the top node of the plan.
* </p>
* <p>
- * This canonical plan, however, is optimized and rearranged so that it performs faster.
+ * This canonical plan, however, is later optimized and rearranged so that it performs
faster.
* </p>
*/
public class CanonicalPlanner implements Planner {
@@ -190,14 +192,15 @@
}
// Validate the source name and set the available columns ...
Table table = context.getSchemata().getTable(selector.getName());
- if (table == null) {
- context.getProblems().addError(GraphI18n.tableDoesNotExist,
-
selector.getName().getString(context.getExecutionContext()));
- } else {
+ if (table != null) {
+ if (table instanceof View) context.getHints().hasView = true;
if (usedSelectors.put(selector.getAliasOrName(), table) != null) {
// There was already a table with this alias or name ...
}
node.setProperty(Property.SOURCE_COLUMNS, table.getColumns());
+ } else {
+ context.getProblems().addError(GraphI18n.tableDoesNotExist,
+
selector.getName().getString(context.getExecutionContext()));
}
return node;
}
@@ -217,7 +220,8 @@
// Handle each child
Source[] clauses = new Source[] {join.getLeft(), join.getRight()};
for (int i = 0; i < 2; i++) {
- node.addLastChild(createPlanNode(context, clauses[i], usedSelectors));
+ PlanNode sourceNode = createPlanNode(context, clauses[i],
usedSelectors);
+ node.addLastChild(sourceNode);
// Add selectors to the joinNode
for (PlanNode child : node.getChildren()) {
@@ -269,6 +273,7 @@
}
});
+ criteriaNode.addFirstChild(plan);
plan = criteriaNode;
}
return plan;
@@ -379,6 +384,7 @@
}
} else {
// Add the selector used by each column ...
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
for (Column column : columns) {
SelectorName tableName = column.getSelectorName();
// Add the selector that is being used ...
@@ -391,9 +397,11 @@
tableName.getString(context.getExecutionContext()));
} else {
// Make sure that the column is in the table ...
- if (table.getColumn(column.getColumnName()) == null) {
+ Name columnName = column.getPropertyName();
+ String name = stringFactory.create(columnName);
+ if (table.getColumn(name) == null) {
context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable,
- column.getColumnName(),
+ name,
tableName.getString(context.getExecutionContext()));
}
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java 2009-10-15
19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -28,19 +28,19 @@
@NotThreadSafe
public final class PlanHints {
- // This flag indicates that the plan has a criteria somewhere
+ /** This flag indicates that the plan has a criteria somewhere */
public boolean hasCriteria = false;
- // This flag indicates that the plan has a join somewhere
+ /** This flag indicates that the plan has a join somewhere */
public boolean hasJoin = false;
- // This flag indicates that the plan has a sort somewhere
+ /** This flag indicates that the plan has a sort somewhere */
public boolean hasSort = false;
// List of groups to make dependent
// public List makeDepGroups = null;
- // flag indicates that the plan has a union somewhere
+ /** flag indicates that the plan has a union somewhere */
public boolean hasSetQuery = false;
// flag indicating that the plan has a grouping node somewhere
@@ -55,6 +55,9 @@
public boolean hasFullTextSearch = false;
+ /** Flag indicates that the plan has at least one view somewhere */
+ public boolean hasView = false;
+
public PlanHints() {
}
@@ -62,6 +65,7 @@
public String toString() {
StringBuilder sb = new StringBuilder("PlanHints {");
sb.append(" hasCriteria=").append(hasCriteria);
+ sb.append(", hasView=").append(hasView);
sb.append(", hasJoin=").append(hasJoin);
sb.append(", hasSort=").append(hasSort);
sb.append(", hasSetQuery=").append(hasSetQuery);
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java 2009-10-15
19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -36,6 +36,7 @@
import java.util.Set;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.ObjectUtil;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.query.model.Column;
import org.jboss.dna.graph.query.model.Command;
@@ -54,7 +55,7 @@
* A representation of a single node within a plan tree.
*/
@NotThreadSafe
-public final class PlanNode implements Iterable<PlanNode>, Readable {
+public final class PlanNode implements Iterable<PlanNode>, Readable, Cloneable {
/**
* An enumeration dictating the type of plan tree nodes.
@@ -298,6 +299,70 @@
}
/**
+ * Return true if this node's type does not match the supplied type
+ *
+ * @param type the type to compare
+ * @return true if this node's type is different than that supplied, or false if
they are the same
+ */
+ public boolean isNot( Type type ) {
+ return this.type != type;
+ }
+
+ /**
+ * Return true if this node's type does not match any of the supplied types
+ *
+ * @param first the type to compare
+ * @param rest the additional types to compare
+ * @return true if this node's type is different than all of those supplied, or
false if matches one of the supplied types
+ */
+ public boolean isNotOneOf( Type first,
+ Type... rest ) {
+ return isNotOneOf(EnumSet.of(first, rest));
+ }
+
+ /**
+ * Return true if this node's type does not match any of the supplied types
+ *
+ * @param types the types to compare
+ * @return true if this node's type is different than all of those supplied, or
false if matches one of the supplied types
+ */
+ public boolean isNotOneOf( Set<Type> types ) {
+ return !types.contains(type);
+ }
+
+ /**
+ * Return true if this node's type does match the supplied type
+ *
+ * @param type the type to compare
+ * @return true if this node's type is the same as that supplied, or false if the
types are different
+ */
+ public boolean is( Type type ) {
+ return this.type == type;
+ }
+
+ /**
+ * Return true if this node's type matches one of the supplied types
+ *
+ * @param first the type to compare
+ * @param rest the additional types to compare
+ * @return true if this node's type is one of those supplied, or false otherwise
+ */
+ public boolean isOneOf( Type first,
+ Type... rest ) {
+ return isOneOf(EnumSet.of(first, rest));
+ }
+
+ /**
+ * Return true if this node's type matches one of the supplied types
+ *
+ * @param types the types to compare
+ * @return true if this node's type is one of those supplied, or false otherwise
+ */
+ public boolean isOneOf( Set<Type> types ) {
+ return types.contains(type);
+ }
+
+ /**
* Get the parent of this node.
*
* @return the parent node, or null if this node has no parent
@@ -357,6 +422,7 @@
*/
public void insertAsParent( PlanNode newParent ) {
if (newParent == null) return;
+ newParent.removeFromParent();
if (this.parent != null) {
this.parent.replaceChild(this, newParent);
}
@@ -832,6 +898,34 @@
}
/**
+ * {@inheritDoc}
+ * <p>
+ * This class returns a new clone of the plan tree rooted at this node. However, the
top node of the resulting plan tree (that
+ * is, the node returned from this method) has no parent.
+ * </p>
+ *
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public PlanNode clone() {
+ return cloneWithoutNewParent();
+ }
+
+ protected PlanNode cloneWithoutNewParent() {
+ PlanNode result = new PlanNode(this.type, null, this.selectors);
+ if (this.nodeProperties != null && !this.nodeProperties.isEmpty()) {
+ result.nodeProperties = new HashMap<Property,
Object>(this.nodeProperties);
+ }
+ // Clone the children ...
+ for (PlanNode child : children) {
+ PlanNode childClone = child.cloneWithoutNewParent();
+ // The child has no parent, so add the child to the new result ...
+ result.addLastChild(childClone);
+ }
+ return result;
+ }
+
+ /**
* Determine whether the supplied plan is equivalent to this plan.
*
* @param other the other plan to compare with this instance
@@ -840,9 +934,15 @@
public boolean isSameAs( PlanNode other ) {
if (other == null) return false;
if (this.getType() != other.getType()) return false;
- if (!this.nodeProperties.equals(other.nodeProperties)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.nodeProperties, other.nodeProperties))
return false;
if (!this.getSelectors().equals(other.getSelectors())) return false;
- return this.getChildren().equals(other.getChildren());
+ if (this.getChildCount() != other.getChildCount()) return false;
+ Iterator<PlanNode> thisChildren = this.getChildren().iterator();
+ Iterator<PlanNode> thatChildren = other.getChildren().iterator();
+ while (thisChildren.hasNext() && thatChildren.hasNext()) {
+ if (!thisChildren.next().isSameAs(thatChildren.next())) return false;
+ }
+ return true;
}
/**
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
(rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -0,0 +1,842 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.plan;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.ChildNode;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DescendantNode;
+import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.FullTextSearchScore;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.LowerCase;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Or;
+import org.jboss.dna.graph.query.model.Ordering;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SameNode;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.UpperCase;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.model.Visitors.AbstractVisitor;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+
+/**
+ * Utilities for working with {@link PlanNode}s.
+ */
+public class PlanUtil {
+
+ /**
+ * Collected the minimum set of columns from the supplied table that are required by
or used within the plan at the supplied
+ * node or above. This method first looks to see if the supplied node is a {@link
Type#PROJECT} node, and if so immediately
+ * returns that node's list of {@link Property#PROJECT_COLUMNS projected
columns}. Otherwise, this method finds all of the
+ * {@link Type#SOURCE} nodes below the supplied plan node, and accumulates the set of
sources for which the columns are to be
+ * found. The method then walks up the plan, looking for references to columns of the
supplied table, starting with the
+ * supplied node and walking up until a {@link PlanNode.Type#PROJECT PROJECT} node is
found or the top of the plan is reached.
+ * <p>
+ * The resulting column list will always contain at the front the {@link
Property#PROJECT_COLUMNS columns} from the nearest
+ * PROJECT ancestor, followed by any columns required by criteria. This is done so
that the processing of the nearest PROJECT
+ * ancestor node can simply use the sublist of each tuple.
+ * </p>
+ *
+ * @param context the query context; may not be null
+ * @param planNode the plan node at which the required columns need to be found; may
not be null
+ * @return the list of {@link Column} objects that are used in the plan; never null
but possibly empty if no columns are
+ * actually needed
+ */
+ public static List<Column> findRequiredColumns( QueryContext context,
+ PlanNode planNode ) {
+ List<Column> columns = null;
+ PlanNode node = planNode;
+ PlanNode projectNode = null;
+ // First find the columns from the nearest PROJECT ancestor ...
+ do {
+ switch (node.getType()) {
+ case PROJECT:
+ columns = node.getPropertyAsList(Property.PROJECT_COLUMNS,
Column.class);
+ projectNode = node;
+ node = null;
+ break;
+ default:
+ node = node.getParent();
+ break;
+ }
+ } while (node != null);
+
+ // If the supplied node is a PROJECT, just return the PROJECT node's column
list ...
+ if (projectNode == planNode) return columns;
+
+ // Find the names of the selectors ...
+ Set<SelectorName> names = new HashSet<SelectorName>();
+ for (PlanNode source : planNode.findAllAtOrBelow(Type.SOURCE)) {
+ names.add(source.getProperty(Property.SOURCE_NAME, SelectorName.class));
+ names.add(source.getProperty(Property.SOURCE_ALIAS, SelectorName.class));
+ }
+
+ // Add the PROJECT columns first ...
+ RequiredColumnVisitor collectionVisitor = new
RequiredColumnVisitor(context.getExecutionContext(), names);
+ if (columns != null) {
+ for (Column projectedColumn : columns) {
+ collectionVisitor.visit(projectedColumn);
+ }
+ }
+
+ // Now add the columns from the JOIN and SELECT ancestors ...
+ node = planNode;
+ do {
+ switch (node.getType()) {
+ case JOIN:
+ Constraint criteria = node.getProperty(Property.JOIN_CONSTRAINTS,
Constraint.class);
+ JoinCondition joinCondition =
node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+ Visitors.visitAll(criteria, collectionVisitor);
+ Visitors.visitAll(joinCondition, collectionVisitor);
+ break;
+ case SELECT:
+ criteria = node.getProperty(Property.SELECT_CRITERIA,
Constraint.class);
+ Visitors.visitAll(criteria, collectionVisitor);
+ break;
+ case PROJECT:
+ // Already handled above, but we can stop looking for columns ...
+ return collectionVisitor.getRequiredColumns();
+ default:
+ break;
+ }
+ node = node.getParent();
+ } while (node != null);
+ return collectionVisitor.getRequiredColumns();
+ }
+
+ protected static class RequiredColumnVisitor extends AbstractVisitor {
+ private final ExecutionContext context;
+ private final Set<SelectorName> names;
+ private final List<Column> columns = new LinkedList<Column>();
+ private final Set<Name> requiredColumnNames = new HashSet<Name>();
+
+ protected RequiredColumnVisitor( ExecutionContext context,
+ Set<SelectorName> names ) {
+ this.names = names;
+ this.context = context;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.PropertyExistence)
+ */
+ @Override
+ public void visit( PropertyExistence existence ) {
+ requireColumn(existence.getSelectorName(), existence.getPropertyName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.PropertyValue)
+ */
+ @Override
+ public void visit( PropertyValue value ) {
+ requireColumn(value.getSelectorName(), value.getPropertyName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.EquiJoinCondition)
+ */
+ @Override
+ public void visit( EquiJoinCondition condition ) {
+ requireColumn(condition.getSelector1Name(), condition.getProperty1Name());
+ requireColumn(condition.getSelector2Name(), condition.getProperty2Name());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.Column)
+ */
+ @Override
+ public void visit( Column column ) {
+ requireColumn(column.getSelectorName(), column.getPropertyName());
+ }
+
+ protected void requireColumn( SelectorName selector,
+ Name propertyName ) {
+ if (names.contains(selector)) {
+ // The column is part of the table we're interested in ...
+ if (requiredColumnNames.add(propertyName)) {
+ String alias =
context.getValueFactories().getStringFactory().create(propertyName);
+ columns.add(new Column(selector, propertyName, alias));
+ }
+ }
+ }
+
+ /**
+ * Get the columns that are required.
+ *
+ * @return the columns; never null
+ */
+ public List<Column> getRequiredColumns() {
+ return columns;
+ }
+ }
+
+ public static void replaceReferencesToRemovedSource( QueryContext context,
+ PlanNode planNode,
+ Map<SelectorName,
SelectorName> rewrittenSelectors ) {
+ switch (planNode.getType()) {
+ case PROJECT:
+ List<Column> columns =
planNode.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+ for (int i = 0; i != columns.size(); ++i) {
+ Column column = columns.get(i);
+ SelectorName replacement =
rewrittenSelectors.get(column.getSelectorName());
+ if (replacement != null) {
+ columns.set(i, new Column(replacement, column.getPropertyName(),
column.getColumnName()));
+ }
+ }
+ break;
+ case SELECT:
+ Constraint constraint = planNode.getProperty(Property.SELECT_CRITERIA,
Constraint.class);
+ Constraint newConstraint = replaceReferencesToRemovedSource(context,
constraint, rewrittenSelectors);
+ if (constraint != newConstraint) {
+ planNode.setProperty(Property.SELECT_CRITERIA, newConstraint);
+ }
+ break;
+ case SORT:
+ List<Object> orderBys =
planNode.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
+ if (orderBys != null && !orderBys.isEmpty()) {
+ if (orderBys.get(0) instanceof SelectorName) {
+ for (int i = 0; i != orderBys.size(); ++i) {
+ SelectorName selectorName = (SelectorName)orderBys.get(i);
+ SelectorName replacement =
rewrittenSelectors.get(selectorName);
+ if (replacement != null) {
+ orderBys.set(i, replacement);
+ }
+ }
+ } else {
+ for (int i = 0; i != orderBys.size(); ++i) {
+ Ordering ordering = (Ordering)orderBys.get(i);
+ DynamicOperand operand = ordering.getOperand();
+ orderBys.set(i, replaceReferencesToRemovedSource(context,
operand, rewrittenSelectors));
+ }
+ }
+ }
+ break;
+ case JOIN:
+ // Update the join condition ...
+ JoinCondition joinCondition =
planNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+ JoinCondition newCondition = replaceReferencesToRemovedSource(context,
joinCondition, rewrittenSelectors);
+ if (joinCondition != newCondition) {
+ planNode.setProperty(Property.JOIN_CONDITION, newCondition);
+ }
+
+ // Update the join criteria (if there are some) ...
+ List<Constraint> constraints =
planNode.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
+ if (constraints != null && !constraints.isEmpty()) {
+ for (int i = 0; i != constraints.size(); ++i) {
+ Constraint old = constraints.get(i);
+ Constraint replacement =
replaceReferencesToRemovedSource(context, old, rewrittenSelectors);
+ if (replacement != old) {
+ constraints.set(i, replacement);
+ }
+ }
+ }
+ break;
+ case GROUP:
+ case SET_OPERATION:
+ case DUP_REMOVE:
+ case LIMIT:
+ case NULL:
+ case SOURCE:
+ case ACCESS:
+ // None of these have to be changed ...
+ break;
+ }
+
+ // Update the selectors referenced by the node ...
+ Set<SelectorName> selectorsToAdd = null;
+ for (Iterator<SelectorName> iter = planNode.getSelectors().iterator();
iter.hasNext();) {
+ SelectorName replacement = rewrittenSelectors.get(iter.next());
+ if (replacement != null) {
+ iter.remove();
+ if (selectorsToAdd == null) selectorsToAdd = new
HashSet<SelectorName>();
+ selectorsToAdd.add(replacement);
+ }
+ }
+ if (selectorsToAdd != null) planNode.getSelectors().addAll(selectorsToAdd);
+
+ // Now call recursively on the children ...
+ for (PlanNode child : planNode) {
+ replaceReferencesToRemovedSource(context, child, rewrittenSelectors);
+ }
+ }
+
+ public static DynamicOperand replaceReferencesToRemovedSource( QueryContext context,
+ DynamicOperand
operand,
+ Map<SelectorName,
SelectorName> rewrittenSelectors ) {
+ if (operand instanceof FullTextSearchScore) {
+ FullTextSearchScore score = (FullTextSearchScore)operand;
+ SelectorName replacement = rewrittenSelectors.get(score.getSelectorName());
+ if (replacement == null) return score;
+ return new FullTextSearchScore(replacement);
+ }
+ if (operand instanceof Length) {
+ Length operation = (Length)operand;
+ PropertyValue wrapped = operation.getPropertyValue();
+ SelectorName replacement =
rewrittenSelectors.get(wrapped.getSelectorName());
+ if (replacement == null) return operand;
+ return new Length(new PropertyValue(replacement,
wrapped.getPropertyName()));
+ }
+ if (operand instanceof LowerCase) {
+ LowerCase operation = (LowerCase)operand;
+ SelectorName replacement =
rewrittenSelectors.get(operation.getSelectorName());
+ if (replacement == null) return operand;
+ return new LowerCase(replaceReferencesToRemovedSource(context,
operation.getOperand(), rewrittenSelectors));
+ }
+ if (operand instanceof UpperCase) {
+ UpperCase operation = (UpperCase)operand;
+ SelectorName replacement =
rewrittenSelectors.get(operation.getSelectorName());
+ if (replacement == null) return operand;
+ return new UpperCase(replaceReferencesToRemovedSource(context,
operation.getOperand(), rewrittenSelectors));
+ }
+ if (operand instanceof NodeName) {
+ NodeName name = (NodeName)operand;
+ SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
+ if (replacement == null) return name;
+ return new NodeName(replacement);
+ }
+ if (operand instanceof NodeLocalName) {
+ NodeLocalName name = (NodeLocalName)operand;
+ SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
+ if (replacement == null) return name;
+ return new NodeLocalName(replacement);
+ }
+ if (operand instanceof PropertyValue) {
+ PropertyValue value = (PropertyValue)operand;
+ SelectorName replacement = rewrittenSelectors.get(value.getSelectorName());
+ if (replacement == null) return operand;
+ return new PropertyValue(replacement, value.getPropertyName());
+ }
+ if (operand instanceof NodeDepth) {
+ NodeDepth depth = (NodeDepth)operand;
+ SelectorName replacement = rewrittenSelectors.get(depth.getSelectorName());
+ if (replacement == null) return operand;
+ return new NodeDepth(replacement);
+ }
+ if (operand instanceof NodePath) {
+ NodePath path = (NodePath)operand;
+ SelectorName replacement = rewrittenSelectors.get(path.getSelectorName());
+ if (replacement == null) return operand;
+ return new NodePath(replacement);
+ }
+ return operand;
+ }
+
+ public static Constraint replaceReferencesToRemovedSource( QueryContext context,
+ Constraint constraint,
+ Map<SelectorName,
SelectorName> rewrittenSelectors ) {
+ if (constraint instanceof And) {
+ And and = (And)constraint;
+ Constraint left = replaceReferencesToRemovedSource(context, and.getLeft(),
rewrittenSelectors);
+ Constraint right = replaceReferencesToRemovedSource(context, and.getRight(),
rewrittenSelectors);
+ if (left == and.getLeft() && right == and.getRight()) return and;
+ return new And(left, right);
+ }
+ if (constraint instanceof Or) {
+ Or or = (Or)constraint;
+ Constraint left = replaceReferencesToRemovedSource(context, or.getLeft(),
rewrittenSelectors);
+ Constraint right = replaceReferencesToRemovedSource(context, or.getRight(),
rewrittenSelectors);
+ if (left == or.getLeft() && right == or.getRight()) return or;
+ return new Or(left, right);
+ }
+ if (constraint instanceof Not) {
+ Not not = (Not)constraint;
+ Constraint wrapped = replaceReferencesToRemovedSource(context,
not.getConstraint(), rewrittenSelectors);
+ if (wrapped == not.getConstraint()) return not;
+ return new Not(wrapped);
+ }
+ if (constraint instanceof SameNode) {
+ SameNode sameNode = (SameNode)constraint;
+ SelectorName replacement =
rewrittenSelectors.get(sameNode.getSelectorName());
+ if (replacement == null) return sameNode;
+ return new SameNode(replacement, sameNode.getPath());
+ }
+ if (constraint instanceof ChildNode) {
+ ChildNode childNode = (ChildNode)constraint;
+ SelectorName replacement =
rewrittenSelectors.get(childNode.getSelectorName());
+ if (replacement == null) return childNode;
+ return new ChildNode(replacement, childNode.getParentPath());
+ }
+ if (constraint instanceof DescendantNode) {
+ DescendantNode descendantNode = (DescendantNode)constraint;
+ SelectorName replacement =
rewrittenSelectors.get(descendantNode.getSelectorName());
+ if (replacement == null) return descendantNode;
+ return new DescendantNode(replacement, descendantNode.getAncestorPath());
+ }
+ if (constraint instanceof PropertyExistence) {
+ PropertyExistence existence = (PropertyExistence)constraint;
+ SelectorName replacement =
rewrittenSelectors.get(existence.getSelectorName());
+ if (replacement == null) return existence;
+ return new PropertyExistence(replacement, existence.getPropertyName());
+ }
+ if (constraint instanceof FullTextSearch) {
+ FullTextSearch search = (FullTextSearch)constraint;
+ SelectorName replacement = rewrittenSelectors.get(search.getSelectorName());
+ if (replacement == null) return search;
+ return new FullTextSearch(replacement, search.getPropertyName(),
search.getFullTextSearchExpression());
+ }
+ if (constraint instanceof Comparison) {
+ Comparison comparison = (Comparison)constraint;
+ DynamicOperand lhs = comparison.getOperand1();
+ StaticOperand rhs = comparison.getOperand2(); // Current only a literal;
therefore, no reference to selector
+ DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs,
rewrittenSelectors);
+ if (lhs == newLhs) return comparison;
+ return new Comparison(newLhs, comparison.getOperator(), rhs);
+ }
+ return constraint;
+ }
+
+ public static JoinCondition replaceReferencesToRemovedSource( QueryContext context,
+ JoinCondition
joinCondition,
+ Map<SelectorName,
SelectorName> rewrittenSelectors ) {
+ if (joinCondition instanceof EquiJoinCondition) {
+ EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
+ SelectorName replacement1 =
rewrittenSelectors.get(condition.getSelector1Name());
+ SelectorName replacement2 =
rewrittenSelectors.get(condition.getSelector2Name());
+ if (replacement1 == condition.getSelector1Name() && replacement2 ==
condition.getSelector2Name()) return condition;
+ return new EquiJoinCondition(replacement1, condition.getProperty1Name(),
replacement2, condition.getProperty2Name());
+ }
+ if (joinCondition instanceof SameNodeJoinCondition) {
+ SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
+ SelectorName replacement1 =
rewrittenSelectors.get(condition.getSelector1Name());
+ SelectorName replacement2 =
rewrittenSelectors.get(condition.getSelector2Name());
+ if (replacement1 == condition.getSelector1Name() && replacement2 ==
condition.getSelector2Name()) return condition;
+ return new SameNodeJoinCondition(replacement1, replacement2,
condition.getSelector2Path());
+ }
+ if (joinCondition instanceof ChildNodeJoinCondition) {
+ ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
+ SelectorName childSelector =
rewrittenSelectors.get(condition.getChildSelectorName());
+ SelectorName parentSelector =
rewrittenSelectors.get(condition.getParentSelectorName());
+ if (childSelector == condition.getChildSelectorName() &&
parentSelector == condition.getParentSelectorName()) return condition;
+ return new ChildNodeJoinCondition(parentSelector, childSelector);
+ }
+ if (joinCondition instanceof DescendantNodeJoinCondition) {
+ DescendantNodeJoinCondition condition =
(DescendantNodeJoinCondition)joinCondition;
+ SelectorName ancestor =
rewrittenSelectors.get(condition.getAncestorSelectorName());
+ SelectorName descendant =
rewrittenSelectors.get(condition.getDescendantSelectorName());
+ if (ancestor == condition.getAncestorSelectorName() && descendant ==
condition.getDescendantSelectorName()) return condition;
+ return new ChildNodeJoinCondition(ancestor, descendant);
+ }
+ return joinCondition;
+ }
+
+ public static void replaceViewReferences( QueryContext context,
+ PlanNode topOfViewInPlan,
+ ColumnMapping mappings ) {
+ assert topOfViewInPlan != null;
+ SelectorName viewName = mappings.getOriginalName();
+
+ // Walk up the plan to change any references to the view columns into references
to the source columns...
+ PlanNode node = topOfViewInPlan;
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
+ List<PlanNode> potentiallyRemovableSources = new
LinkedList<PlanNode>();
+ do {
+ // Remove the view from the selectors ...
+ if (node.getSelectors().contains(viewName)) {
+ switch (node.getType()) {
+ case PROJECT:
+ // Adjust the columns ...
+ node.getSelectors().remove(viewName);
+ List<Column> columns =
node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+ if (columns != null) {
+ for (int i = 0; i != columns.size(); ++i) {
+ Column column = columns.get(i);
+ if (column.getSelectorName().equals(viewName)) {
+ // This column references the view ...
+ String columnName =
stringFactory.create(column.getPropertyName());
+ String columnAlias = column.getColumnName();
+ // Find the source column that this view column
corresponds to ...
+ Column sourceColumn =
mappings.getMappedColumn(columnName);
+ if (sourceColumn != null) {
+ SelectorName sourceName =
sourceColumn.getSelectorName();
+ // Replace the view column with one that uses the
same alias but that references the
+ // source
+ // column ...
+ columns.set(i, new Column(sourceName,
sourceColumn.getPropertyName(), columnAlias));
+ node.addSelector(sourceName);
+ } else {
+ node.addSelector(column.getSelectorName());
+ }
+ } else {
+ node.addSelector(column.getSelectorName());
+ }
+ }
+ }
+ break;
+ case SELECT:
+ Constraint constraint =
node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+ Constraint newConstraint = replaceReferences(context, constraint,
mappings, node);
+ if (constraint != newConstraint) {
+ node.getSelectors().clear();
+
node.addSelectors(Visitors.getSelectorsReferencedBy(newConstraint));
+ node.setProperty(Property.SELECT_CRITERIA, newConstraint);
+ }
+ break;
+ case SET_OPERATION:
+ break;
+ case SOURCE:
+ SelectorName sourceName = node.getProperty(Property.SOURCE_NAME,
SelectorName.class);
+ assert sourceName.equals(sourceName); // selector name already
matches
+ potentiallyRemovableSources.add(node);
+ break;
+ default:
+ break;
+ }
+ if (!node.getSelectors().contains(viewName)) {
+ // Used to reference view, so it needs to at least reference
+ }
+ }
+ // Move to the parent ...
+ node = node.getParent();
+ } while (node != null);
+
+ // Remove any source node that was the view but that is no longer used in any
higher node ...
+ for (PlanNode sourceNode : potentiallyRemovableSources) {
+ boolean stillRequired = false;
+ node = sourceNode.getParent();
+ while (node != null) {
+ if (node.getSelectors().contains(viewName)) {
+ stillRequired = true;
+ break;
+ }
+ node = node.getParent();
+ }
+ if (!stillRequired) {
+ assert sourceNode.getParent() != null;
+ sourceNode.extractFromParent();
+ }
+ }
+ }
+
+ public static Constraint replaceReferences( QueryContext context,
+ Constraint constraint,
+ ColumnMapping mapping,
+ PlanNode node ) {
+ if (constraint instanceof And) {
+ And and = (And)constraint;
+ Constraint left = replaceReferences(context, and.getLeft(), mapping, node);
+ Constraint right = replaceReferences(context, and.getRight(), mapping,
node);
+ if (left == and.getLeft() && right == and.getRight()) return and;
+ return new And(left, right);
+ }
+ if (constraint instanceof Or) {
+ Or or = (Or)constraint;
+ Constraint left = replaceReferences(context, or.getLeft(), mapping, node);
+ Constraint right = replaceReferences(context, or.getRight(), mapping, node);
+ if (left == or.getLeft() && right == or.getRight()) return or;
+ return new Or(left, right);
+ }
+ if (constraint instanceof Not) {
+ Not not = (Not)constraint;
+ Constraint wrapped = replaceReferences(context, not.getConstraint(), mapping,
node);
+ return wrapped == not.getConstraint() ? not : new Not(wrapped);
+ }
+ if (constraint instanceof SameNode) {
+ SameNode sameNode = (SameNode)constraint;
+ if (!mapping.getOriginalName().equals(sameNode.getSelectorName())) return
sameNode;
+ if (!mapping.isMappedToSingleSelector()) return sameNode;
+ SelectorName selector = mapping.getSingleMappedSelectorName();
+ node.addSelector(selector);
+ return new SameNode(selector, sameNode.getPath());
+ }
+ if (constraint instanceof ChildNode) {
+ ChildNode childNode = (ChildNode)constraint;
+ if (!mapping.getOriginalName().equals(childNode.getSelectorName())) return
childNode;
+ if (!mapping.isMappedToSingleSelector()) return childNode;
+ SelectorName selector = mapping.getSingleMappedSelectorName();
+ node.addSelector(selector);
+ return new ChildNode(selector, childNode.getParentPath());
+ }
+ if (constraint instanceof DescendantNode) {
+ DescendantNode descendantNode = (DescendantNode)constraint;
+ if (!mapping.getOriginalName().equals(descendantNode.getSelectorName()))
return descendantNode;
+ if (!mapping.isMappedToSingleSelector()) return descendantNode;
+ SelectorName selector = mapping.getSingleMappedSelectorName();
+ node.addSelector(selector);
+ return new DescendantNode(selector, descendantNode.getAncestorPath());
+ }
+ if (constraint instanceof PropertyExistence) {
+ PropertyExistence existence = (PropertyExistence)constraint;
+ if (!mapping.getOriginalName().equals(existence.getSelectorName())) return
existence;
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
+ Column sourceColumn =
mapping.getMappedColumn(stringFactory.create(existence.getPropertyName()));
+ if (sourceColumn == null) return existence;
+ node.addSelector(sourceColumn.getSelectorName());
+ return new PropertyExistence(sourceColumn.getSelectorName(),
sourceColumn.getPropertyName());
+ }
+ if (constraint instanceof FullTextSearch) {
+ FullTextSearch search = (FullTextSearch)constraint;
+ if (!mapping.getOriginalName().equals(search.getSelectorName())) return
search;
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
+ Column sourceColumn =
mapping.getMappedColumn(stringFactory.create(search.getPropertyName()));
+ if (sourceColumn == null) return search;
+ node.addSelector(sourceColumn.getSelectorName());
+ return new FullTextSearch(sourceColumn.getSelectorName(),
sourceColumn.getPropertyName(),
+ search.getFullTextSearchExpression());
+ }
+ if (constraint instanceof Comparison) {
+ Comparison comparison = (Comparison)constraint;
+ DynamicOperand lhs = comparison.getOperand1();
+ StaticOperand rhs = comparison.getOperand2(); // Current only a literal;
therefore, no reference to selector
+ DynamicOperand newLhs = replaceViewReferences(context, lhs, mapping, node);
+ if (lhs == newLhs) return comparison;
+ return new Comparison(newLhs, comparison.getOperator(), rhs);
+ }
+ return constraint;
+ }
+
+ public static DynamicOperand replaceViewReferences( QueryContext context,
+ DynamicOperand operand,
+ ColumnMapping mapping,
+ PlanNode node ) {
+ if (operand instanceof FullTextSearchScore) {
+ FullTextSearchScore score = (FullTextSearchScore)operand;
+ if (!mapping.getOriginalName().equals(score.getSelectorName())) return
score;
+ if (!mapping.isMappedToSingleSelector()) return score;
+ return new FullTextSearchScore(mapping.getSingleMappedSelectorName());
+ }
+ if (operand instanceof Length) {
+ Length operation = (Length)operand;
+ return new Length((PropertyValue)replaceViewReferences(context,
operation.getPropertyValue(), mapping, node));
+ }
+ if (operand instanceof LowerCase) {
+ LowerCase operation = (LowerCase)operand;
+ return new LowerCase(replaceViewReferences(context, operation.getOperand(),
mapping, node));
+ }
+ if (operand instanceof UpperCase) {
+ UpperCase operation = (UpperCase)operand;
+ return new UpperCase(replaceViewReferences(context, operation.getOperand(),
mapping, node));
+ }
+ if (operand instanceof NodeName) {
+ NodeName name = (NodeName)operand;
+ if (!mapping.getOriginalName().equals(name.getSelectorName())) return name;
+ if (!mapping.isMappedToSingleSelector()) return name;
+ node.addSelector(mapping.getSingleMappedSelectorName());
+ return new NodeName(mapping.getSingleMappedSelectorName());
+ }
+ if (operand instanceof NodeLocalName) {
+ NodeLocalName name = (NodeLocalName)operand;
+ if (!mapping.getOriginalName().equals(name.getSelectorName())) return name;
+ if (!mapping.isMappedToSingleSelector()) return name;
+ node.addSelector(mapping.getSingleMappedSelectorName());
+ return new NodeLocalName(mapping.getSingleMappedSelectorName());
+ }
+ if (operand instanceof PropertyValue) {
+ PropertyValue value = (PropertyValue)operand;
+ if (!mapping.getOriginalName().equals(value.getSelectorName())) return
value;
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
+ Column sourceColumn =
mapping.getMappedColumn(stringFactory.create(value.getPropertyName()));
+ if (sourceColumn == null) return value;
+ node.addSelector(sourceColumn.getSelectorName());
+ return new PropertyValue(sourceColumn.getSelectorName(),
sourceColumn.getPropertyName());
+ }
+ if (operand instanceof NodeDepth) {
+ NodeDepth depth = (NodeDepth)operand;
+ if (!mapping.getOriginalName().equals(depth.getSelectorName())) return
depth;
+ if (!mapping.isMappedToSingleSelector()) return depth;
+ node.addSelector(mapping.getSingleMappedSelectorName());
+ return new NodeDepth(mapping.getSingleMappedSelectorName());
+ }
+ if (operand instanceof NodePath) {
+ NodePath path = (NodePath)operand;
+ if (!mapping.getOriginalName().equals(path.getSelectorName())) return path;
+ if (!mapping.isMappedToSingleSelector()) return path;
+ node.addSelector(mapping.getSingleMappedSelectorName());
+ return new NodePath(mapping.getSingleMappedSelectorName());
+ }
+ return operand;
+ }
+
+ public static JoinCondition replaceReferences( QueryContext context,
+ JoinCondition joinCondition,
+ ColumnMapping mapping,
+ PlanNode node ) {
+ if (joinCondition instanceof EquiJoinCondition) {
+ EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
+ SelectorName replacement1 = condition.getSelector1Name();
+ SelectorName replacement2 = condition.getSelector2Name();
+ Name property1 = condition.getProperty1Name();
+ Name property2 = condition.getProperty2Name();
+ if (replacement1.equals(mapping.getOriginalName())) {
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
+ Column sourceColumn =
mapping.getMappedColumn(stringFactory.create(property1));
+ if (sourceColumn != null) {
+ replacement1 = sourceColumn.getSelectorName();
+ property1 = sourceColumn.getPropertyName();
+ }
+ }
+ if (replacement2.equals(mapping.getOriginalName())) {
+ ValueFactory<String> stringFactory =
context.getExecutionContext().getValueFactories().getStringFactory();
+ Column sourceColumn =
mapping.getMappedColumn(stringFactory.create(property2));
+ if (sourceColumn != null) {
+ replacement2 = sourceColumn.getSelectorName();
+ property2 = sourceColumn.getPropertyName();
+ }
+ }
+ if (replacement1 == condition.getSelector1Name() && replacement2 ==
condition.getSelector2Name()) return condition;
+ node.addSelector(replacement1, replacement2);
+ return new EquiJoinCondition(replacement1, property1, replacement2,
property2);
+ }
+ // All the remaining conditions can only be rewritten if there is a single source
...
+ if (!mapping.isMappedToSingleSelector()) return joinCondition;
+
+ // There is only a single source ...
+ SelectorName viewName = mapping.getOriginalName();
+ SelectorName sourceName = mapping.getSingleMappedSelectorName();
+
+ if (joinCondition instanceof SameNodeJoinCondition) {
+ SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
+ SelectorName replacement1 = condition.getSelector1Name();
+ SelectorName replacement2 = condition.getSelector2Name();
+ if (replacement1.equals(viewName)) replacement1 = sourceName;
+ if (replacement2.equals(viewName)) replacement2 = sourceName;
+ if (replacement1 == condition.getSelector1Name() && replacement2 ==
condition.getSelector2Name()) return condition;
+ node.addSelector(replacement1, replacement2);
+ return new SameNodeJoinCondition(replacement1, replacement2,
condition.getSelector2Path());
+ }
+ if (joinCondition instanceof ChildNodeJoinCondition) {
+ ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
+ SelectorName childSelector = condition.getChildSelectorName();
+ SelectorName parentSelector = condition.getParentSelectorName();
+ if (childSelector.equals(viewName)) childSelector = sourceName;
+ if (parentSelector.equals(viewName)) parentSelector = sourceName;
+ if (childSelector == condition.getChildSelectorName() &&
parentSelector == condition.getParentSelectorName()) return condition;
+ node.addSelector(childSelector, parentSelector);
+ return new ChildNodeJoinCondition(parentSelector, childSelector);
+ }
+ if (joinCondition instanceof DescendantNodeJoinCondition) {
+ DescendantNodeJoinCondition condition =
(DescendantNodeJoinCondition)joinCondition;
+ SelectorName ancestor = condition.getAncestorSelectorName();
+ SelectorName descendant = condition.getDescendantSelectorName();
+ if (ancestor.equals(viewName)) ancestor = sourceName;
+ if (descendant.equals(viewName)) descendant = sourceName;
+ if (ancestor == condition.getAncestorSelectorName() && descendant ==
condition.getDescendantSelectorName()) return condition;
+ node.addSelector(ancestor, descendant);
+ return new ChildNodeJoinCondition(ancestor, descendant);
+ }
+ return joinCondition;
+ }
+
+ public static ColumnMapping createMappingFor( View view,
+ PlanNode viewPlan ) {
+ ColumnMapping mapping = new ColumnMapping(view.getName());
+
+ // Find the PROJECT node in the view plan ...
+ PlanNode project = viewPlan.findAtOrBelow(Type.PROJECT);
+ assert project != null;
+
+ // Get the Columns from the PROJECT in the plan node ...
+ List<Column> projectedColumns =
project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+
+ // Get the Schemata columns defined by the view ...
+ List<org.jboss.dna.graph.query.validate.Schemata.Column> viewColumns =
view.getColumns();
+ assert viewColumns.size() == projectedColumns.size();
+
+ for (int i = 0; i != viewColumns.size(); ++i) {
+ Column projectedColunn = projectedColumns.get(i);
+ String viewColumnName = viewColumns.get(i).getName();
+ mapping.map(viewColumnName, projectedColunn);
+ }
+ return mapping;
+ }
+
+ /**
+ * Defines how the view columns are mapped (or resolved) into the columns from the
source tables.
+ */
+ public static class ColumnMapping {
+ private final SelectorName originalName;
+ private final Map<String, Column> mappedColumnsByOriginalColumnName = new
HashMap<String, Column>();
+ private final Set<SelectorName> mappedSelectorNames = new
HashSet<SelectorName>();
+
+ public ColumnMapping( SelectorName originalName ) {
+ this.originalName = originalName;
+ }
+
+ public void map( String originalColumnName,
+ Column projectedColumn ) {
+ mappedColumnsByOriginalColumnName.put(originalColumnName, projectedColumn);
+ mappedSelectorNames.add(projectedColumn.getSelectorName());
+ }
+
+ public SelectorName getOriginalName() {
+ return originalName;
+ }
+
+ public Column getMappedColumn( String viewColumnName ) {
+ return mappedColumnsByOriginalColumnName.get(viewColumnName);
+ }
+
+ public boolean isMappedToSingleSelector() {
+ return mappedSelectorNames.size() == 1;
+ }
+
+ /**
+ * @return tableNames
+ */
+ public Set<SelectorName> getMappedSelectorNames() {
+ return mappedSelectorNames;
+ }
+
+ public SelectorName getSingleMappedSelectorName() {
+ return isMappedToSingleSelector() ? mappedSelectorNames.iterator().next() :
null;
+ }
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -32,10 +32,23 @@
import java.util.Set;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.text.ParsingException;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.parse.InvalidQueryException;
+import org.jboss.dna.graph.query.parse.SqlQueryParser;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
/**
* An immutable {@link Schemata} implementation.
@@ -46,10 +59,13 @@
/**
* Obtain a new instance for building Schemata objects.
*
+ * @param context the execution context that can be used when building the schema
* @return the new builder; never null
+ * @throws IllegalArgumentException if the context is null
*/
- public static Builder createBuilder() {
- return new Builder();
+ public static Builder createBuilder( ExecutionContext context ) {
+ CheckArg.isNotNull(context, "context");
+ return new Builder(context);
}
/**
@@ -58,8 +74,14 @@
@NotThreadSafe
public static class Builder {
- private Map<SelectorName, ImmutableTable> tables = new
HashMap<SelectorName, ImmutableTable>();
+ private final ExecutionContext context;
+ private final Map<SelectorName, ImmutableTable> tables = new
HashMap<SelectorName, ImmutableTable>();
+ private final Map<SelectorName, QueryCommand> viewDefinitions = new
HashMap<SelectorName, QueryCommand>();
+ protected Builder( ExecutionContext context ) {
+ this.context = context;
+ }
+
/**
* Add a table with the supplied name and column names. Each column will be given
a type of {@link PropertyType#STRING}.
* The table will also overwrite any existing table definition with the same
name.
@@ -254,6 +276,43 @@
}
/**
+ * Add a view with the supplied name and SQL string definition. The column names
and types will be inferred from the
+ * source table(s) and views(s) used in the definition.
+ *
+ * @param name the name of the new view
+ * @param definition the SQL definition of the view
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the view name is null or empty or the
definition is null
+ * @throws ParsingException if the supplied definition is cannot be parsed as a
SQL query
+ */
+ public Builder addView( String name,
+ String definition ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotEmpty(definition, "definition");
+ SqlQueryParser parser = new SqlQueryParser();
+ QueryCommand command = parser.parseQuery(definition, context);
+ this.viewDefinitions.put(new SelectorName(name), command);
+ return this;
+ }
+
+ /**
+ * Add a view with the supplied name and definition. The column names and types
will be inferred from the source table(s)
+ * used in the definition.
+ *
+ * @param name the name of the new view
+ * @param definition the definition of the view
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the view name is null or empty or the
definition is null
+ */
+ public Builder addView( String name,
+ QueryCommand definition ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotNull(definition, "definition");
+ this.viewDefinitions.put(new SelectorName(name), definition);
+ return this;
+ }
+
+ /**
* Add a column with the supplied name and type to the named table. Any existing
column with that name will be replaced
* with the new column. If the table does not yet exist, it will be added.
*
@@ -320,9 +379,70 @@
* tables (with their columns) as they exist at the moment this method is
called.
*
* @return the new Schemata; never null
+ * @throws InvalidQueryException if any of the view definitions is invalid and
cannot be resolved
*/
public Schemata build() {
- return new ImmutableSchemata(new HashMap<SelectorName,
Table>(tables));
+ ImmutableSchemata schemata = new ImmutableSchemata(new
HashMap<SelectorName, Table>(tables));
+
+ // Make a copy of the view definitions, and create the views ...
+ Map<SelectorName, QueryCommand> definitions = new
HashMap<SelectorName, QueryCommand>(viewDefinitions);
+ ValueFactory<String> stringFactory =
context.getValueFactories().getStringFactory();
+ boolean added = false;
+ do {
+ added = false;
+ Set<SelectorName> viewNames = new
HashSet<SelectorName>(definitions.keySet());
+ for (SelectorName name : viewNames) {
+ QueryCommand command = definitions.get(name);
+ // Create the canonical plan for the definition ...
+ QueryContext queryContext = new QueryContext(context, new
PlanHints(), schemata);
+ CanonicalPlanner planner = new CanonicalPlanner();
+ PlanNode plan = planner.createPlan(queryContext, command);
+ if (queryContext.getProblems().hasErrors()) continue;
+
+ // Get the columns from the top-level PROJECT ...
+ PlanNode project = plan.findAtOrBelow(Type.PROJECT);
+ assert project != null;
+ List<org.jboss.dna.graph.query.model.Column> columns =
project.getPropertyAsList(Property.PROJECT_COLUMNS,
+
org.jboss.dna.graph.query.model.Column.class);
+ assert !columns.isEmpty();
+
+ // Go through all the columns and look up the types ...
+ List<Column> viewColumns = new
ArrayList<Column>(columns.size());
+ for (org.jboss.dna.graph.query.model.Column column : columns) {
+ // Find the table that the column came from ...
+ Table source = schemata.getTable(column.getSelectorName());
+ if (source == null) break;
+ String viewColumnName = column.getColumnName();
+ String sourceColumnName =
stringFactory.create(column.getPropertyName()); // getColumnName() returns alias
+ Column sourceColumn = source.getColumn(sourceColumnName);
+ if (sourceColumn == null) {
+ throw new InvalidQueryException(Visitors.readable(command),
+ "The view references a
non-existant column '"
+ + column.getColumnName() +
"' in '" + source.getName() + "'");
+ }
+ viewColumns.add(new ImmutableColumn(viewColumnName,
sourceColumn.getPropertyType()));
+ }
+ if (viewColumns.size() != columns.size()) {
+ // We weren't able to resolve all of the columns,
+ // so maybe the columns were referencing yet-to-be-built views
...
+ continue;
+ }
+
+ // If we could resolve the definition ...
+ ImmutableView view = new ImmutableView(name, viewColumns, command);
+ definitions.remove(name);
+ schemata = schemata.with(view);
+ added = true;
+ }
+ } while (added && !definitions.isEmpty());
+
+ if (!definitions.isEmpty()) {
+ QueryCommand command = definitions.values().iterator().next();
+ throw new InvalidQueryException(Visitors.readable(command), "The
view definition cannot be resolved: "
+ +
Visitors.readable(command));
+ }
+
+ return schemata;
}
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -187,6 +187,14 @@
return new ImmutableTable(getName(), newColumns);
}
+ public ImmutableTable withColumns( Iterable<Column> columns ) {
+ List<Column> newColumns = new LinkedList<Column>(this.getColumns());
+ for (Column column : columns) {
+ newColumns.add(new ImmutableColumn(column.getName(),
column.getPropertyType()));
+ }
+ return new ImmutableTable(getName(), newColumns);
+ }
+
public ImmutableTable with( SelectorName name ) {
return new ImmutableTable(name, columnsByName, columns, keys);
}
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -0,0 +1,107 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.validate;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.validate.Schemata.Column;
+import org.jboss.dna.graph.query.validate.Schemata.Key;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+
+/**
+ *
+ */
+@Immutable
+class ImmutableView extends ImmutableTable implements View {
+
+ private final QueryCommand definition;
+
+ protected ImmutableView( SelectorName name,
+ Iterable<Column> columns,
+ QueryCommand definition ) {
+ super(name, columns);
+ this.definition = definition;
+ }
+
+ protected ImmutableView( SelectorName name,
+ Iterable<Column> columns,
+ QueryCommand definition,
+ Iterable<Column>... keyColumns ) {
+ super(name, columns, keyColumns);
+ this.definition = definition;
+ }
+
+ protected ImmutableView( SelectorName name,
+ Map<String, Column> columnsByName,
+ List<Column> columns,
+ QueryCommand definition,
+ Set<Key> keys ) {
+ super(name, columnsByName, columns, keys);
+ this.definition = definition;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.View#getDefinition()
+ */
+ public QueryCommand getDefinition() {
+ return definition;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(getName().getName());
+ sb.append('(');
+ boolean first = true;
+ for (Column column : getColumns()) {
+ if (first) first = false;
+ else sb.append(", ");
+ sb.append(column);
+ }
+ sb.append(") AS '");
+ sb.append(Visitors.readable(definition));
+ sb.append('\'');
+ if (!getKeys().isEmpty()) {
+ sb.append(" with keys ");
+ first = true;
+ for (Key key : getKeys()) {
+ if (first) first = false;
+ else sb.append(", ");
+ sb.append(key);
+ }
+ }
+ return sb.toString();
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -28,6 +28,7 @@
import java.util.Map;
import java.util.Set;
import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.model.QueryCommand;
import org.jboss.dna.graph.query.model.SelectorName;
/**
@@ -36,10 +37,14 @@
public interface Schemata {
/**
- * Get the information for the table with the supplied name within this schema.
+ * Get the information for the table or view with the supplied name within this
schema.
+ * <p>
+ * The resulting definition is immutable, though subsequent calls to this method with
the same argument may result in a
+ * different definition.
+ * </p>
*
- * @param name the table name; may not be null
- * @return the table information, or null if there is no such table
+ * @param name the table or view name; may not be null
+ * @return the table or view information, or null if there is no such table
*/
Table getTable( SelectorName name );
@@ -56,6 +61,9 @@
/**
* Get the information for a column with the supplied name within this table.
+ * <p>
+ * The resulting column definition is immutable.
+ * </p>
*
* @param name the column name; may not be null
* @return the column information, or null if there is no such column
@@ -65,21 +73,21 @@
/**
* Get the queryable columns in this table.
*
- * @return the map of column objects by their name; never null
+ * @return the immutable map of immutable column objects by their name; never
null
*/
Map<String, Column> getColumnsByName();
/**
* Get the queryable columns in this table.
*
- * @return the ordered column objects; never null
+ * @return the immutable, ordered list of immutable column objects; never null
*/
List<Column> getColumns();
/**
* Get the collection of keys for this table.
*
- * @return the immutable collection of keys; never null, but possibly empty
+ * @return the immutable collection of immutable keys; never null, but possibly
empty
*/
Collection<Key> getKeys();
@@ -101,6 +109,9 @@
/**
* Obtain this table's {@link #getKeys() key} that contains exactly those
columns listed.
+ * <p>
+ * The resulting key definition is immutable.
+ * </p>
*
* @param columns the columns for the key
* @return the key that uses exactly the supplied columns, or null if there is no
such key
@@ -109,6 +120,9 @@
/**
* Obtain this table's {@link #getKeys() key} that contains exactly those
columns listed.
+ * <p>
+ * The resulting key definition is immutable.
+ * </p>
*
* @param columns the columns for the key
* @return the key that uses exactly the supplied columns, or null if there is no
such key
@@ -117,6 +131,18 @@
}
/**
+ * Information about a view that is defined in terms of other views/tables.
+ */
+ public interface View extends Table {
+ /**
+ * Get the {@link QueryCommand query} that is the definition of the view.
+ *
+ * @return the view definition; never null
+ */
+ QueryCommand getDefinition();
+ }
+
+ /**
* Information about a queryable column.
*/
public interface Column {
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -25,8 +25,9 @@
* This package provides the interfaces that define the tables and columns that can be
queried. Though the query framework does not prescribe
* the semantics of a table or column, it does provide
* a {@link org.jboss.dna.graph.query.validate.Schemata} interface that provides access
to the immutable
- * {@link org.jboss.dna.graph.query.validate.Schemata.Table} definitions (that then
contain the
- * {@link org.jboss.dna.graph.query.validate.Schemata.Column} and {@link
org.jboss.dna.graph.query.validate.Schemata.Key} definitions).
+ * {@link org.jboss.dna.graph.query.validate.Schemata.Table} and {@link
org.jboss.dna.graph.query.validate.Schemata.View}
+ * definitions (that then contain the {@link
org.jboss.dna.graph.query.validate.Schemata.Column}
+ * and {@link org.jboss.dna.graph.query.validate.Schemata.Key} definitions).
* <p>
* Although any Schemata implementaiton can be used, the query framework provides an
{@link org.jboss.dna.graph.query.validate.ImmutableSchemata}
* class with a {@link org.jboss.dna.graph.query.validate.ImmutableSchemata.Builder
builder} with a fluent API
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -27,6 +27,7 @@
import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -51,6 +52,14 @@
}
}
+ protected void assertSameChildren( PlanNode node,
+ PlanNode... children ) {
+ assertThat(node.getChildCount(), is(children.length));
+ for (int i = 0; i != node.getChildCount(); ++i) {
+ assertThat(node.getChild(i).isSameAs(children[i]), is(true));
+ }
+ }
+
protected void assertSelectors( PlanNode node,
String... selectors ) {
Set<SelectorName> selectorSet = new HashSet<SelectorName>();
@@ -60,6 +69,19 @@
assertThat("Selectors don't match", node.getSelectors(),
is(selectorSet));
}
+ protected void assertProperty( PlanNode node,
+ Property name,
+ Object value ) {
+ assertThat("Property value doesn't match", node.getProperty(name),
is(value));
+ }
+
+ protected <T> void assertPropertyIsList( PlanNode node,
+ Property name,
+ Class<T> valueType,
+ T... values ) {
+ assertThat("Property value doesn't match",
node.getPropertyAsList(name, valueType), is(Arrays.asList(values)));
+ }
+
protected void assertSortOrderBy( PlanNode sortNode,
String... selectors ) {
List<SelectorName> expected = new
ArrayList<SelectorName>(selectors.length);
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -0,0 +1,165 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.ImmutableSchemata;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ReplaceViewsTest extends AbstractQueryTest {
+
+ private ReplaceViews rule;
+ private QueryContext context;
+ private Schemata schemata;
+ private ImmutableSchemata.Builder builder;
+
+ @Before
+ public void beforeEach() {
+ ExecutionContext execContext = new ExecutionContext();
+ rule = ReplaceViews.INSTANCE;
+ builder = ImmutableSchemata.createBuilder(execContext);
+ builder.addTable("t1", "c11", "c12",
"c13");
+ builder.addTable("t2", "c21", "c22",
"c23");
+ builder.addView("v1", "SELECT c11, c12 FROM t1 WHERE c13 <
CAST('3' AS LONG)");
+ builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN
t2 ON t1.c11 = t2.c21");
+ schemata = builder.build();
+ context = new QueryContext(execContext, new PlanHints(), schemata);
+ }
+
+ /**
+ * Before:
+ *
+ * <pre>
+ * ...
+ * |
+ * PROJECT with the list of columns being SELECTed
+ * |
+ * SELECT1
+ * | One or more SELECT plan nodes that each have
+ * SELECT2 a single non-join constraint that are then all AND-ed
+ * | together
+ * SELECT3
+ * |
+ * SOURCE A SOURCE that uses the view
+ * </pre>
+ *
+ * And after:
+ *
+ * <pre>
+ * ...
+ * |
+ * PROJECT with the list of columns being SELECTed
+ * |
+ * SELECT1
+ * | One or more SELECT plan nodes that each have
+ * SELECT2 a single non-join constraint that are then all AND-ed
+ * | together
+ * SELECT3
+ * |
+ * SOURCE
+ * |
+ * PROJECT with the list of columns in the SELECT of the view
+ * |
+ * SELECT1 One or more SELECT plan nodes that each have a single non-join
constraint
+ * | that are then all AND-ed together (as defined by the view
plan
+ * |
+ * SOURCE A SOURCE that the view uses
+ * </pre>
+ */
+ @Test
+ public void shouldReplaceViewWithPlanForViewWithSingleTable() {
+ // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the
selectors that they apply to ...
+ PlanNode project = new PlanNode(Type.PROJECT, selector("v1"));
+ PlanNode select1 = new PlanNode(Type.SELECT, project, selector("v1"));
+ PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("v1"));
+ PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("v1"));
+ PlanNode source = new PlanNode(Type.SOURCE, select3, selector("v1"));
+ source.setProperty(Property.SOURCE_NAME, selector("v1"));
+
+ // Create the equivalent plan nodes for what should be created ...
+ PlanNode viewProject = new PlanNode(Type.PROJECT, selector("t1"));
+ PlanNode viewSelect = new PlanNode(Type.SELECT, viewProject,
selector("t1"));
+ PlanNode viewSource = new PlanNode(Type.SOURCE, viewSelect,
selector("t1"));
+ viewProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11"), column("t1", "c12")));
+ viewSelect.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t1"), name("c13")),
+
Operator.LESS_THAN, new Literal(3L)));
+ viewSource.setProperty(Property.SOURCE_NAME, selector("t1"));
+ viewSource.setProperty(Property.SOURCE_COLUMNS,
schemata.getTable(selector("t1")).getColumns());
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, project, new
LinkedList<OptimizerRule>());
+ System.out.println(result);
+ assertThat(result, is(sameInstance(project)));
+ assertChildren(project, select1);
+ assertChildren(select1, select2);
+ assertChildren(select2, select3);
+ assertChildren(select3, source);
+ assertSameChildren(source, viewSelect);
+ assertSameChildren(source.getFirstChild(), viewSource);
+ assertSameChildren(source.getFirstChild().getFirstChild());
+ }
+
+ protected List<Column> columns( Column... columns ) {
+ return Arrays.asList(columns);
+ }
+
+ protected Column column( String table,
+ String columnName ) {
+ return new Column(new SelectorName(table), name(columnName), columnName);
+ }
+
+ protected Column column( String table,
+ String columnName,
+ String alias ) {
+ return new Column(new SelectorName(table), name(columnName), alias);
+ }
+
+ protected Name name( String name ) {
+ return
context.getExecutionContext().getValueFactories().getNameFactory().create(name);
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -25,18 +25,33 @@
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import org.jboss.dna.common.collection.Problems;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.AbstractQueryTest;
import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.parse.SqlQueryParser;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.JoinAlgorithm;
import org.jboss.dna.graph.query.plan.PlanHints;
import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.ImmutableSchemata;
import org.jboss.dna.graph.query.validate.Schemata;
import org.junit.Before;
import org.junit.Test;
@@ -44,7 +59,7 @@
/**
*
*/
-public class RuleBasedOptimizerTest {
+public class RuleBasedOptimizerTest extends AbstractQueryTest {
private RuleBasedOptimizer optimizer;
private List<OptimizerRule> rules;
@@ -54,7 +69,15 @@
@Before
public void beforeEach() {
- context = new QueryContext(new ExecutionContext(), new PlanHints(),
mock(Schemata.class));
+ ExecutionContext execContext = new ExecutionContext();
+ ImmutableSchemata.Builder builder =
ImmutableSchemata.createBuilder(execContext);
+ builder.addTable("t1", "c11", "c12",
"c13");
+ builder.addTable("t2", "c21", "c22",
"c23");
+ builder.addView("v1", "SELECT c11, c12 AS c2 FROM t1 WHERE c13
< CAST('3' AS LONG)");
+ builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN
t2 ON t1.c11 = t2.c21");
+ Schemata schemata = builder.build();
+ context = new QueryContext(execContext, new PlanHints(), schemata);
+
node = new PlanNode(Type.ACCESS);
ruleExecutionOrder = new ArrayList<Integer>();
@@ -85,6 +108,10 @@
};
}
+ //
----------------------------------------------------------------------------------------------------------------
+ // Test the rule stack logic
+ //
----------------------------------------------------------------------------------------------------------------
+
@Test
public void shouldExecuteEachRuleInSequence() {
optimizer.optimize(context, node);
@@ -111,4 +138,184 @@
assertThat(ruleExecutionOrder.get(2), is(2));
assertThat(ruleExecutionOrder.size(), is(3));
}
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Test the actual rules
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldOptimizePlanForSimpleQueryWithSelectColumns() {
+ node = optimize("SELECT c11,c12 FROM t1");
+ // Create the expected plan ...
+ PlanNode expected = new PlanNode(Type.ACCESS, selector("t1"));
+ PlanNode project = new PlanNode(Type.PROJECT, expected,
selector("t1"));
+ project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11"), column("t1", "c12")));
+ PlanNode source = new PlanNode(Type.SOURCE, project, selector("t1"));
+ source.setProperty(Property.SOURCE_NAME, selector("t1"));
+ source.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t1")).getColumns());
+ // Compare the expected and actual plan ...
+ assertThat(node.isSameAs(expected), is(true));
+ }
+
+ @Test
+ public void shouldOptimizePlanForSimpleQueryWithSelectStar() {
+ node = optimize("SELECT * FROM t1");
+ // Create the expected plan ...
+ PlanNode expected = new PlanNode(Type.ACCESS, selector("t1"));
+ PlanNode project = new PlanNode(Type.PROJECT, expected,
selector("t1"));
+ project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11"), column("t1", "c12"), column("t1",
"c13")));
+ PlanNode source = new PlanNode(Type.SOURCE, project, selector("t1"));
+ source.setProperty(Property.SOURCE_NAME, selector("t1"));
+ source.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t1")).getColumns());
+ // Compare the expected and actual plan ...
+ assertThat(node.isSameAs(expected), is(true));
+ }
+
+ @Test
+ public void shouldOptimizePlanForSimpleQueryWithPropertyValueCriteria() {
+ node = optimize("SELECT c11, c12 FROM t1 WHERE c13 < CAST('3' AS
LONG)");
+ // Create the expected plan ...
+ PlanNode expected = new PlanNode(Type.ACCESS, selector("t1"));
+ PlanNode project = new PlanNode(Type.PROJECT, expected,
selector("t1"));
+ project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11"), column("t1", "c12")));
+ PlanNode select = new PlanNode(Type.SELECT, project, selector("t1"));
+ select.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t1"), name("c13")),
+ Operator.LESS_THAN,
new Literal(3L)));
+ PlanNode source = new PlanNode(Type.SOURCE, select, selector("t1"));
+ source.setProperty(Property.SOURCE_NAME, selector("t1"));
+ source.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t1")).getColumns());
+ // Compare the expected and actual plan ...
+ assertThat(node.isSameAs(expected), is(true));
+ }
+
+ @Test
+ public void shouldOptimizePlanForEquiJoinQuery() {
+ node = optimize("SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 =
t2.c21");
+
+ // Create the expected plan ...
+ PlanNode project = new PlanNode(Type.PROJECT, selector("t2"),
selector("t1"));
+ project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11"), column("t1", "c12"), column("t2",
"c23")));
+ PlanNode join = new PlanNode(Type.JOIN, project, selector("t2"),
selector("t1"));
+ join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+ join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+ join.setProperty(Property.JOIN_CONDITION, new
EquiJoinCondition(selector("t1"), name("c11"),
selector("t2"), name("c21")));
+
+ PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("t1"));
+ PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess,
selector("t1"));
+ leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11"), column("t1", "c12")));
+ PlanNode leftSource = new PlanNode(Type.SOURCE, leftProject,
selector("t1"));
+ leftSource.setProperty(Property.SOURCE_NAME, selector("t1"));
+ leftSource.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t1")).getColumns());
+
+ PlanNode rightAccess = new PlanNode(Type.ACCESS, join,
selector("t2"));
+ PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess,
selector("t2"));
+ rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t2",
"c23"), column("t2", "c21")));
+ PlanNode rightSource = new PlanNode(Type.SOURCE, rightProject,
selector("t2"));
+ rightSource.setProperty(Property.SOURCE_NAME, selector("t2"));
+ rightSource.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t2")).getColumns());
+
+ // Compare the expected and actual plan ...
+ assertThat(node.isSameAs(project), is(true));
+ }
+
+ @Test
+ public void shouldOptimizePlanForQueryUsingView() {
+ node = optimize("SELECT v1.c11 AS c1 FROM v1 WHERE v1.c11 = 'x' AND
v1.c2 = 'y'");
+
+ // Create the expected plan ...
+ PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+ PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+ project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11", "c1")));
+ PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+ select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t1"), name("c11")),
+ Operator.EQUAL_TO,
new Literal('x')));
+ PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+ select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t1"), name("c12")),
+ Operator.EQUAL_TO,
new Literal('y')));
+ PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+ select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t1"), name("c13")),
+ Operator.LESS_THAN,
new Literal(3L)));
+ PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+ source.setProperty(Property.SOURCE_NAME, selector("t1"));
+ source.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t1")).getColumns());
+
+ // Compare the expected and actual plan ...
+ assertThat(node.isSameAs(access), is(true));
+ }
+
+ @Test
+ public void shouldOptimizePlanForQueryUsingViewContainingJoin() {
+ node = optimize("SELECT v2.c11 AS c1 FROM v2 WHERE v2.c11 = 'x' AND
v2.c12 = 'y'");
+
+ // Create the expected plan ...
+ PlanNode project = new PlanNode(Type.PROJECT, selector("t1"));
+ project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11", "c1")));
+ PlanNode join = new PlanNode(Type.JOIN, project, selector("t2"),
selector("t1"));
+ join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+ join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+ join.setProperty(Property.JOIN_CONDITION, new
EquiJoinCondition(selector("t1"), name("c11"),
selector("t2"), name("c21")));
+
+ PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("t1"));
+ PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess,
selector("t1"));
+ leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1",
"c11")));
+ PlanNode leftSelect1 = new PlanNode(Type.SELECT, leftProject,
selector("t1"));
+ leftSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t1"), name("c11")),
+
Operator.EQUAL_TO, new Literal('x')));
+ PlanNode leftSelect2 = new PlanNode(Type.SELECT, leftSelect1,
selector("t1"));
+ leftSelect2.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t1"), name("c12")),
+
Operator.EQUAL_TO, new Literal('y')));
+ PlanNode leftSource = new PlanNode(Type.SOURCE, leftSelect2,
selector("t1"));
+ leftSource.setProperty(Property.SOURCE_NAME, selector("t1"));
+ leftSource.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t1")).getColumns());
+
+ PlanNode rightAccess = new PlanNode(Type.ACCESS, join,
selector("t2"));
+ PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess,
selector("t2"));
+ rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t2",
"c21")));
+ PlanNode rightSelect1 = new PlanNode(Type.SELECT, rightProject,
selector("t2"));
+ rightSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new
PropertyValue(selector("t2"), name("c21")),
+
Operator.EQUAL_TO, new Literal('x')));
+ PlanNode rightSource = new PlanNode(Type.SOURCE, rightSelect1,
selector("t2"));
+ rightSource.setProperty(Property.SOURCE_NAME, selector("t2"));
+ rightSource.setProperty(Property.SOURCE_COLUMNS,
context.getSchemata().getTable(selector("t2")).getColumns());
+
+ // Compare the expected and actual plan ...
+ assertThat(node.isSameAs(project), is(true));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Utility methods ...
+ //
----------------------------------------------------------------------------------------------------------------
+
+ protected List<Column> columns( Column... columns ) {
+ return Arrays.asList(columns);
+ }
+
+ protected Column column( String table,
+ String columnName ) {
+ return new Column(new SelectorName(table), name(columnName), columnName);
+ }
+
+ protected Column column( String table,
+ String columnName,
+ String alias ) {
+ return new Column(new SelectorName(table), name(columnName), alias);
+ }
+
+ protected Name name( String name ) {
+ return
context.getExecutionContext().getValueFactories().getNameFactory().create(name);
+ }
+
+ protected PlanNode optimize( String sql ) {
+ QueryCommand query = new SqlQueryParser().parseQuery(sql,
context.getExecutionContext());
+ Problems problems = context.getProblems();
+ assertThat("Problems parsing query: " + sql + "\n" +
problems, problems.hasErrors(), is(false));
+ PlanNode plan = new CanonicalPlanner().createPlan(context, query);
+ assertThat("Problems planning query: " + sql + "\n" +
problems, problems.hasErrors(), is(false));
+ PlanNode optimized = new RuleBasedOptimizer().optimize(context, plan);
+ assertThat("Problems optimizing query: " + sql + "\n" +
problems, problems.hasErrors(), is(false));
+ System.out.println();
+ System.out.println(sql);
+ System.out.println(optimized);
+ return optimized;
+ }
}
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -35,9 +35,6 @@
import org.jboss.dna.graph.query.model.Column;
import org.jboss.dna.graph.query.model.QueryCommand;
import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.plan.PlanNode;
import org.jboss.dna.graph.query.plan.PlanNode.Property;
import org.jboss.dna.graph.query.plan.PlanNode.Type;
import org.jboss.dna.graph.query.validate.ImmutableSchemata;
@@ -68,7 +65,7 @@
hints = new PlanHints();
builder = new QueryBuilder(context);
problems = new SimpleProblems();
- schemataBuilder = ImmutableSchemata.createBuilder();
+ schemataBuilder = ImmutableSchemata.createBuilder(context);
}
protected SelectorName selector( String name ) {
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java 2009-10-15
19:55:58 UTC (rev 1293)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -69,7 +69,7 @@
protected Schemata schemataFor( Columns columns,
PropertyType... types ) {
- ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder();
+ ImmutableSchemata.Builder builder =
ImmutableSchemata.createBuilder(executionContext);
for (String selectorName : columns.getSelectorNames()) {
final SelectorName selector = selector(selectorName);
int i = 0;
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java 2009-10-15
19:57:20 UTC (rev 1294)
@@ -0,0 +1,179 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.validate;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.validate.Schemata.Table;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ImmutableSchemataBuilderTest {
+
+ private ExecutionContext context;
+ private ImmutableSchemata.Builder builder;
+ private Schemata schemata;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ builder = ImmutableSchemata.createBuilder(context);
+ }
+
+ @After
+ public void afterEach() {
+ context = null;
+ builder = null;
+ schemata = null;
+ }
+
+ @Test
+ public void shouldBuildSchemaForSingleTableWithDefaultTypeForColumns() {
+ builder.addTable("t1", "c1", "c2",
"c3");
+ schemata = builder.build();
+ Table table = schemata.getTable(selector("t1"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c2").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c3").getPropertyType(),
is(PropertyType.STRING));
+ }
+
+ @Test
+ public void shouldBuildSchemaForMultipleTablesWithDefaultTypeForColumns() {
+ builder.addTable("t1", "c11", "c12",
"c13");
+ builder.addTable("t2", "c21", "c22",
"c23");
+ schemata = builder.build();
+ Table table = schemata.getTable(selector("t1"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c11").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c12").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c13").getPropertyType(),
is(PropertyType.STRING));
+ table = schemata.getTable(selector("t2"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c21").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c22").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c23").getPropertyType(),
is(PropertyType.STRING));
+ }
+
+ @Test
+ public void shouldBuildSchemaContainingViewOfSingleTable() {
+ builder.addTable("t1", "c1", "c2",
"c3");
+ builder.addView("t2", "SELECT * FROM t1 WHERE c1=3");
+ schemata = builder.build();
+ Table table = schemata.getTable(selector("t1"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c2").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c3").getPropertyType(),
is(PropertyType.STRING));
+ table = schemata.getTable(selector("t2"));
+ assertThat(table, is(instanceOf(View.class)));
+ assertThat(table.getColumn("c1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c2").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c3").getPropertyType(),
is(PropertyType.STRING));
+ }
+
+ @Test
+ public void shouldBuildSchemaContainingViewUsingAliasesOfColumns() {
+ builder.addTable("t1", "c1", "c2",
"c3");
+ builder.addView("t2", "SELECT c1 as v1, c2 FROM t1 WHERE
c1=3");
+ schemata = builder.build();
+ Table table = schemata.getTable(selector("t1"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c2").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c3").getPropertyType(),
is(PropertyType.STRING));
+ table = schemata.getTable(selector("t2"));
+ assertThat(table, is(instanceOf(View.class)));
+ assertThat(table.getColumn("v1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c2").getPropertyType(),
is(PropertyType.STRING));
+ }
+
+ @Test
+ public void shouldBuildSchemaContainingViewOfMultiTableJoin() {
+ builder.addTable("t1", "c11", "c12",
"c13");
+ builder.addTable("t2", "c21", "c22",
"c23");
+ builder.addView("v1", "SELECT t1.c11 as x1, t1.c12, t2.c23 FROM t1
JOIN t2 ON t1.c11=t2.c21 WHERE t1.c11=3");
+ schemata = builder.build();
+ Table table = schemata.getTable(selector("t1"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c11").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c12").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c13").getPropertyType(),
is(PropertyType.STRING));
+ table = schemata.getTable(selector("t2"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c21").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c22").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c23").getPropertyType(),
is(PropertyType.STRING));
+ schemata = builder.build();
+ table = schemata.getTable(selector("v1"));
+ assertThat(table, is(instanceOf(View.class)));
+ assertThat(table.getColumn("x1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c12").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c23").getPropertyType(),
is(PropertyType.STRING));
+ }
+
+ @Test
+ public void shouldBuildSchemaContainingViewUsingView() {
+ builder.addTable("t1", "c11", "c12",
"c13");
+ builder.addTable("t2", "c21", "c22",
"c23");
+ builder.addView("v2", "SELECT x1, c12 FROM v1 WHERE x1=3");
+ builder.addView("v1", "SELECT t1.c11 as x1, t1.c12, t2.c23 FROM t1
JOIN t2 ON t1.c11=t2.c21 WHERE t1.c11=3");
+ schemata = builder.build();
+ Table table = schemata.getTable(selector("t1"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c11").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c12").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c13").getPropertyType(),
is(PropertyType.STRING));
+ table = schemata.getTable(selector("t2"));
+ assertThat(table, is(notNullValue()));
+ assertThat(table.getColumn("c21").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c22").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c23").getPropertyType(),
is(PropertyType.STRING));
+ schemata = builder.build();
+ table = schemata.getTable(selector("v1"));
+ assertThat(table, is(instanceOf(View.class)));
+ assertThat(table.getColumn("x1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c12").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c23").getPropertyType(),
is(PropertyType.STRING));
+ table = schemata.getTable(selector("v2"));
+ assertThat(table, is(instanceOf(View.class)));
+ assertThat(table.getColumn("x1").getPropertyType(),
is(PropertyType.STRING));
+ assertThat(table.getColumn("c12").getPropertyType(),
is(PropertyType.STRING));
+ }
+
+ protected SelectorName selector( String name ) {
+ return new SelectorName(name);
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF