Skip to content

Commit

Permalink
MONDRIAN: Fix bug 793616: Deeply nested UNION function takes forever …
Browse files Browse the repository at this point in the history
…to validate.

[git-p4: depot-paths = "//open/mondrian/": change = 727]
  • Loading branch information
julianhyde committed Aug 23, 2003
1 parent bc740dc commit e89d796
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/main/mondrian/olap/Query.java
Expand Up @@ -1392,10 +1392,24 @@ public Hierarchy[] getMdxHierarchiesOnAxis(int axis)
}
}

/**
* Default implementation of {@link Exp.Resolver}.
*
* <p>Uses a stack to help us guess the type of our parent expression
* before we've completely resolved our children -- necessary,
* unfortunately, when figuring out whether the "*" operator denotes
* multiplication or crossjoin.
*
* <p>Keeps track of which nodes have already been resolved, so we don't
* try to resolve nodes which have already been resolved. (That would not
* be wrong, but can cause resolution to be an <code>O(2^N)</code>
* operation.)
*/
private class StackResolver implements Exp.Resolver {
private final Stack stack = new Stack();
private final FunTable funTable;
private boolean haveCollectedParameters;
private HashSet resolvedNodes = new HashSet();

public StackResolver(FunTable funTable) {
this.funTable = funTable;
Expand All @@ -1406,24 +1420,37 @@ public Query getQuery() {
}

public Exp resolveChild(Exp exp) {
if (!resolvedNodes.add(exp)) {
return exp; // already resolved
}
stack.push(exp);
try {
return exp.resolve(this);
final Exp resolved = exp.resolve(this);
resolvedNodes.add(resolved);
return resolved;
} finally {
stack.pop();
}
}

public Parameter resolveChild(Parameter parameter) {
if (!resolvedNodes.add(parameter)) {
return parameter; // already resolved
}
stack.push(parameter);
try {
return (Parameter) parameter.resolve(this);
final Parameter resolved = (Parameter) parameter.resolve(this);
resolvedNodes.add(resolved);
return resolved;
} finally {
stack.pop();
}
}

public void resolveChild(MemberProperty memberProperty) {
if (!resolvedNodes.add(memberProperty)) {
return; // already resolved
}
stack.push(memberProperty);
try {
memberProperty.resolve(this);
Expand All @@ -1433,6 +1460,9 @@ public void resolveChild(MemberProperty memberProperty) {
}

public void resolveChild(QueryAxis axis) {
if (!resolvedNodes.add(axis)) {
return; // already resolved
}
stack.push(axis);
try {
axis.resolve(this);
Expand All @@ -1442,6 +1472,9 @@ public void resolveChild(QueryAxis axis) {
}

public void resolveChild(Formula formula) {
if (!resolvedNodes.add(formula)) {
return; // already resolved
}
stack.push(formula);
try {
formula.resolve(this);
Expand Down
88 changes: 88 additions & 0 deletions src/main/mondrian/test/BasicQueryTest.java
Expand Up @@ -2069,6 +2069,94 @@ public void testBug769114() {
"Row #9: 45,185.41" + nl);
}

/**
* Bug 793616: Deeply nested UNION function takes forever to validate.
* (Problem was that each argument of a function was validated twice, hence
* the validation time was <code>O(2 ^ depth)</code>.)
*/
public void testBug793616() {
Result result = runQuery("select {[Measures].[Unit Sales]," + nl +
" [Measures].[Store Cost]," + nl +
" [Measures].[Store Sales]} ON columns," + nl +
"Hierarchize(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union" + nl +
"({([Gender].[All Gender]," + nl +
" [Marital Status].[All Marital Status]," + nl +
" [Customers].[All Customers]," + nl +
" [Product].[All Products])}," + nl +
" Crossjoin ([Gender].[All Gender].Children," + nl +
" {([Marital Status].[All Marital Status]," + nl +
" [Customers].[All Customers]," + nl +
" [Product].[All Products])}))," + nl +
" Crossjoin(Crossjoin({[Gender].[All Gender].[F]}," + nl +
" [Marital Status].[All Marital Status].Children)," + nl +
" {([Customers].[All Customers]," + nl +
" [Product].[All Products])}))," + nl +
" Crossjoin(Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status].[M])}," + nl +
" [Customers].[All Customers].Children)," + nl +
" {[Product].[All Products]}))," + nl +
" Crossjoin(Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status].[M])}," + nl +
" [Customers].[All Customers].[USA].Children)," + nl +
" {[Product].[All Products]}))," + nl +
" Crossjoin ({([Gender].[All Gender].[F], [Marital Status].[All Marital Status].[M], [Customers].[All Customers].[USA].[CA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin({([Gender].[All Gender].[F], [Marital Status].[All Marital Status].[M], [Customers].[All Customers].[USA].[OR])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin({([Gender].[All Gender].[F], [Marital Status].[All Marital Status].[M], [Customers].[All Customers].[USA].[WA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin ({([Gender].[All Gender].[F], [Marital Status].[All Marital Status].[M], [Customers].[All Customers].[USA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin(" + nl +
" Crossjoin({([Gender].[All Gender].[F], [Marital Status].[All Marital Status].[S])}, [Customers].[All Customers].Children)," + nl +
" {[Product].[All Products]}))," + nl +
" Crossjoin(Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status].[S])}," + nl +
" [Customers].[All Customers].[USA].Children)," + nl +
" {[Product].[All Products]}))," + nl +
" Crossjoin ({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status].[S]," + nl +
" [Customers].[All Customers].[USA].[CA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status].[S]," + nl +
" [Customers].[All Customers].[USA].[OR])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status].[S]," + nl +
" [Customers].[All Customers].[USA].[WA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin ({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status].[S]," + nl +
" [Customers].[All Customers].[USA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin(Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status])}," + nl +
" [Customers].[All Customers].Children)," + nl +
" {[Product].[All Products]}))," + nl +
" Crossjoin(Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status])}," + nl +
" [Customers].[All Customers].[USA].Children)," + nl +
" {[Product].[All Products]}))," + nl +
" Crossjoin ({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status]," + nl +
" [Customers].[All Customers].[USA].[CA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status]," + nl +
" [Customers].[All Customers].[USA].[OR])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status]," + nl +
" [Customers].[All Customers].[USA].[WA])}," + nl +
" [Product].[All Products].Children))," + nl +
" Crossjoin ({([Gender].[All Gender].[F]," + nl +
" [Marital Status].[All Marital Status]," + nl +
" [Customers].[All Customers].[USA])}," + nl +
" [Product].[All Products].Children))) ON rows from [Sales] where [Time].[1997]");
assertEquals(59, result.getAxes()[1].positions.length);
}

public void testCatalogHierarchyBasedOnView() {
Schema schema = getConnection().getSchema();
final Cube salesCube = schema.lookupCube("Sales", true);
Expand Down

0 comments on commit e89d796

Please sign in to comment.