Skip to content

Commit

Permalink
Ensure that SelectNode's filter axis is always set. If the axis has a…
Browse files Browse the repository at this point in the history
… null

expression, that means there is no WHERE clause. You can assign the expression
after tha axis has been created, including to null.
Fixes bug 2789893, "SelectNode should have method setFilterAxis".


git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@233 c6a108a4-781c-0410-a6c6-c2d559e19af0
  • Loading branch information
julianhyde committed May 12, 2009
1 parent b41879b commit a1810a4
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 21 deletions.
5 changes: 4 additions & 1 deletion src/org/olap4j/Axis.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ public interface Axis {
* Enumeration of standard, named axes descriptors.
*/
public enum Standard implements Axis {
/** Filter axis, also known as the slicer axis. */
/**
* Filter axis, also known as the slicer axis, and represented by the
* WHERE clause of an MDX query.
*/
FILTER,

/** COLUMNS axis, also known as X axis and AXIS(0). */
Expand Down
10 changes: 6 additions & 4 deletions src/org/olap4j/mdx/AxisNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
package org.olap4j.mdx;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;

Expand Down Expand Up @@ -38,22 +37,25 @@ public class AxisNode implements ParseTreeNode {
* @param region Region of source code
* @param nonEmpty Whether to filter out members of this axis whose cells
* are all empty
* @param axisDef Which axis (ROWS, COLUMNS, etc.)
* @param axis Which axis (ROWS, COLUMNS, etc.)
* @param dimensionProperties List of dimension properties; if null,
* empty list is assumed
* @param expression Expression to populate the axis
*/
public AxisNode(
ParseRegion region,
boolean nonEmpty,
Axis axisDef,
Axis axis,
List<IdentifierNode> dimensionProperties,
ParseTreeNode expression)
{
this.region = region;
this.nonEmpty = nonEmpty;
this.expression = expression;
this.axis = axisDef;
this.axis = axis;
if (axis == null) {
throw new IllegalArgumentException("Axis type must not be null");
}
if (dimensionProperties == null) {
dimensionProperties = Collections.emptyList();
}
Expand Down
32 changes: 26 additions & 6 deletions src/org/olap4j/mdx/SelectNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
package org.olap4j.mdx;

import org.olap4j.type.Type;
import org.olap4j.Axis;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.*;

/**
* Parse tree model for an MDX SELECT statement.
Expand Down Expand Up @@ -54,6 +54,19 @@ public SelectNode(
this.withList = withList;
this.axisList = axisList;
this.from = from;
if (filterAxis == null) {
filterAxis =
new AxisNode(
null,
false,
Axis.FILTER,
Collections.<IdentifierNode>emptyList(),
null);
}
if (filterAxis.getAxis() != Axis.FILTER) {
throw new IllegalArgumentException(
"Filter axis must have type FILTER");
}
this.filterAxis = filterAxis;
this.cellPropertyList = cellPropertyList;
}
Expand Down Expand Up @@ -117,7 +130,7 @@ public void unparse(ParseTreeWriter writer) {
pw.println();
pw.print("FROM ");
from.unparse(writer);
if (filterAxis != null) {
if (filterAxis.getExpression() != null) {
pw.println();
pw.print("WHERE ");
filterAxis.unparse(writer);
Expand Down Expand Up @@ -170,8 +183,15 @@ public List<AxisNode> getAxisList() {
}

/**
* Returns the filter axis defined by the WHERE clause of this SelectNode,
* or null if there is no filter axis.
* Returns the filter axis defined by the WHERE clause of this SelectNode.
*
* <p>Never returns {@code null}. If there is no WHERE clause, returns an
* AxisNode for which {@link org.olap4j.mdx.AxisNode#getExpression()}
* returns null.
*
* <p>You can modify the filter expression by calling
* {@link org.olap4j.mdx.AxisNode#getExpression()} on the filter AxisNode;
* {@code null} means that there is no filter axis.
*
* @return filter axis
*/
Expand Down Expand Up @@ -220,7 +240,7 @@ public SelectNode deepCopy() {
MdxUtil.deepCopyList(withList),
MdxUtil.deepCopyList(axisList),
this.from != null ? this.from.deepCopy() : null,
this.filterAxis != null ? this.filterAxis.deepCopy() : null,
this.filterAxis.deepCopy(),
MdxUtil.deepCopyList(cellPropertyList));
}
}
Expand Down
100 changes: 90 additions & 10 deletions testsrc/org/olap4j/ConnectionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1100,27 +1100,35 @@ public void testParsing() throws SQLException {
fail("expected exception, got " + select);
} catch (Exception e) {
assertTrue(
TestContext.getStackTrace(e).indexOf("Duplicate axis name 'COLUMNS'.")
>= 0);
TestContext.getStackTrace(e)
.indexOf("Duplicate axis name 'COLUMNS'.") >= 0);
}
}

private void checkUnparsedMdx(SelectNode select) {
checkUnparsedMdx(
select,
"WITH\n"
+ "MEMBER [Measures].[Foo] AS '[Measures].[Bar]', FORMAT_STRING = \"xxx\"\n"
+ "SELECT\n"
+ "{[Gender]} ON COLUMNS,\n"
+ "{[Store].Children} ON ROWS\n"
+ "FROM [sales]\n"
+ "WHERE [Time].[1997].[Q4]");
}

private void checkUnparsedMdx(
SelectNode select,
String expectedMdx)
{
StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(pw);
select.unparse(parseTreeWriter);
pw.flush();
String mdx = sw.toString();
TestContext.assertEqualsVerbose(
TestContext.fold("WITH\n" +
"MEMBER [Measures].[Foo] AS '[Measures].[Bar]', FORMAT_STRING = \"xxx\"\n" +
"SELECT\n" +
"{[Gender]} ON COLUMNS,\n" +
"{[Store].Children} ON ROWS\n" +
"FROM [sales]\n" +
"WHERE [Time].[1997].[Q4]"),
mdx);
TestContext.fold(expectedMdx), mdx);
}

/**
Expand Down Expand Up @@ -1197,6 +1205,78 @@ public void testUnparsing() {
checkUnparsedMdx(select);
}

public void testBuildParseTree() {
// It is an error to create a select node with a filter axis whose type
// is not filter
try {
SelectNode select = new SelectNode(
null,
new ArrayList<ParseTreeNode>(),
new ArrayList<AxisNode>(),
new IdentifierNode(new IdentifierNode.NameSegment("sales")),
new AxisNode(
null,
false,
Axis.COLUMNS,
new ArrayList<IdentifierNode>(),
null),
new ArrayList<IdentifierNode>());
fail("expected error, got " + select);
} catch (IllegalArgumentException e) {
TestContext.checkThrowable(e, "Filter axis must have type FILTER");
}

// Create a select node with empty filter axis. It is populated with
// a filter axis whose expression is null.
SelectNode select = new SelectNode(
null,
new ArrayList<ParseTreeNode>(),
new ArrayList<AxisNode>(),
new IdentifierNode(new IdentifierNode.NameSegment("sales")),
new AxisNode(
null,
false,
Axis.FILTER,
new ArrayList<IdentifierNode>(),
null),
new ArrayList<IdentifierNode>());
final AxisNode filterAxis = select.getFilterAxis();
assertNotNull(filterAxis);
assertNull(filterAxis.getExpression());
assertEquals(Axis.FILTER, filterAxis.getAxis());

// Parses to an expression with no WHERE clause
checkUnparsedMdx(
select,
"SELECT\n"
+ "FROM [sales]");

// Set the filter, see if it takes.
select.getFilterAxis().setExpression(
new CallNode(
null,
"()",
Syntax.Parentheses,
new IdentifierNode(
new IdentifierNode.NameSegment("Measures"),
new IdentifierNode.NameSegment("Store Sales")),
new IdentifierNode(
new IdentifierNode.NameSegment("Gender"),
new IdentifierNode.NameSegment("M"))));
checkUnparsedMdx(
select,
"SELECT\n"
+ "FROM [sales]\n"
+ "WHERE ([Measures].[Store Sales], [Gender].[M])");

// Set it back to null
select.getFilterAxis().setExpression(null);
checkUnparsedMdx(
select,
"SELECT\n"
+ "FROM [sales]");
}

/**
* Tests the {@link Cube#lookupMember(String[])} method.
*/
Expand Down
20 changes: 20 additions & 0 deletions testsrc/org/olap4j/test/TestContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import org.apache.commons.dbcp.*;
import junit.framework.ComparisonFailure;
import junit.framework.Assert;

/**
* Context for olap4j tests.
Expand Down Expand Up @@ -398,6 +399,25 @@ public static String getStackTrace(Throwable e) {
return sw.toString();
}

/**
* Checks that an exception is not null and the stack trace contains a
* given string. Fails otherwise.
*
* @param throwable Stack trace
* @param pattern Seek string
*/
public static void checkThrowable(Throwable throwable, String pattern) {
if (throwable == null) {
Assert.fail("query did not yield an exception");
}
String stackTrace = getStackTrace(throwable);
if (stackTrace.indexOf(pattern) < 0) {
Assert.fail(
"error does not match pattern '" + pattern
+ "'; error is [" + stackTrace + "]");
}
}

/**
* Returns this context's tester.
*
Expand Down

0 comments on commit a1810a4

Please sign in to comment.