Skip to content

Commit

Permalink
Fix bug 3035910, "Cannot parse sub-query".
Browse files Browse the repository at this point in the history
(Fix contributed by Thomas Klute.)
ParseTreeWriter can now generate indented output.


git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@333 c6a108a4-781c-0410-a6c6-c2d559e19af0
  • Loading branch information
julianhyde committed Jul 30, 2010
1 parent d651165 commit 09ba75f
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -2180,7 +2180,7 @@ public SelectNode validateSelect(
throws OlapException
{
StringWriter sw = new StringWriter();
selectNode.unparse(new ParseTreeWriter(new PrintWriter(sw)));
selectNode.unparse(new ParseTreeWriter(sw));
String mdx = sw.toString();
final XmlaOlap4jConnection olap4jConnection =
(XmlaOlap4jConnection) connection;
Expand Down
4 changes: 1 addition & 3 deletions src/org/olap4j/driver/xmla/XmlaOlap4jStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,10 +405,8 @@ byte[] getBytes() throws OlapException {
*/
private static String toString(ParseTreeNode node) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(pw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(sw);
node.unparse(parseTreeWriter);
pw.flush();
return sw.toString();
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/org/olap4j/mdx/MdxUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ static String quoteForMdx(String val) {

static String toString(ParseTreeNode node) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(pw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(sw);
node.unparse(parseTreeWriter);
pw.flush();
return sw.toString();
}

Expand Down
58 changes: 53 additions & 5 deletions src/org/olap4j/mdx/ParseTreeWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package org.olap4j.mdx;

import java.io.PrintWriter;
import java.io.Writer;

/**
* Writer for MDX parse tree.
Expand Down Expand Up @@ -38,24 +39,71 @@
*/
public class ParseTreeWriter {
private final PrintWriter pw;
private int linePrefixLength;
private String linePrefix;

private static final int INDENT = 4;
private static String bigString = " ";

/**
* Creates a ParseTreeWriter.
*
* @param pw Underlying writer
* @param w Underlying writer
*/
public ParseTreeWriter(PrintWriter pw) {
this.pw = pw;
public ParseTreeWriter(Writer w) {
this.pw = new PrintWriter(w) {
@Override
public void println() {
super.println();
print(linePrefix);
}

};
this.linePrefixLength = 0;
setPrefix();
}

/**
* Returns the underlying writer.
* Returns the print writer.
*
* @return underlying writer
* @return print writer
*/
public PrintWriter getPrintWriter() {
return pw;
}

/**
* Increases the indentation level.
*/
public void indent() {
linePrefixLength += INDENT;
setPrefix();
}

private void setPrefix() {
linePrefix = spaces(linePrefixLength);
}

/**
* Decreases the indentation level.
*/
public void outdent() {
linePrefixLength -= INDENT;
setPrefix();
}

/**
* Returns a string of N spaces.
* @param n Number of spaces
* @return String of N spaces
*/
private static synchronized String spaces(int n)
{
while (n > bigString.length()) {
bigString = bigString + bigString;
}
return bigString.substring(0, n);
}
}

// End ParseTreeWriter.java
24 changes: 16 additions & 8 deletions src/org/olap4j/mdx/SelectNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class SelectNode implements ParseTreeNode {
* @param withList List of members and sets defined in this query using
* a <code>WITH</code> clause
* @param axisList List of axes
* @param from Name of cube
* @param from Contents of FROM clause (name of cube, or subquery)
* @param filterAxis Filter axis
* @param cellPropertyList List of properties
*/
Expand Down Expand Up @@ -102,9 +102,8 @@ public Type getType() {

public String toString() {
StringWriter sw = new StringWriter();
ParseTreeWriter pw = new ParseTreeWriter(new PrintWriter(sw));
ParseTreeWriter pw = new ParseTreeWriter(sw);
unparse(pw);
sw.flush();
return sw.toString();
}

Expand All @@ -129,7 +128,15 @@ public void unparse(ParseTreeWriter writer) {
}
pw.println();
pw.print("FROM ");
from.unparse(writer);
if (from instanceof SelectNode) {
writer.indent();
pw.println("(");
from.unparse(writer);
pw.print(")");
writer.outdent();
} else {
from.unparse(writer);
}
if (filterAxis.getExpression() != null) {
pw.println();
pw.print("WHERE ");
Expand Down Expand Up @@ -201,7 +208,8 @@ public AxisNode getFilterAxis() {

/**
* Returns the node representing the FROM clause of this SELECT statement.
* The node is typically an {@link IdentifierNode} or a {@link CubeNode}.
* The node is typically an {@link IdentifierNode}, a {@link CubeNode} or
* a {@link SelectNode}.
*
* @return FROM clause
*/
Expand All @@ -217,10 +225,10 @@ public ParseTreeNode getFrom() {
* a {@link org.olap4j.mdx.CubeNode} referencing an explicit
* {@link org.olap4j.metadata.Cube} object.
*
* @param fromNode FROM clause
* @param from FROM clause
*/
public void setFrom(ParseTreeNode fromNode) {
this.from = fromNode;
public void setFrom(ParseTreeNode from) {
this.from = from;
}

/**
Expand Down
22 changes: 19 additions & 3 deletions src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ parser code {:
ParseRegion region,
List<ParseTreeNode> withList,
List<AxisNode> axisList,
IdentifierNode cubeName,
ParseTreeNode from,
ParseTreeNode filter,
List<IdentifierNode> cellProps)
{
Expand All @@ -155,7 +155,7 @@ parser code {:
}
});
return new SelectNode(
region, withList, axisList, cubeName, filterAxis, cellProps);
region, withList, axisList, from, filterAxis, cellProps);
}

// Override lr_parser methods for NLS. With this error handling scheme,
Expand Down Expand Up @@ -334,6 +334,7 @@ non terminal AxisNode
axis_specification;
non terminal ParseTreeNode
case_expression,
cube_specification_or_select_statement,
else_clause_opt,
expression,
expression_or_empty,
Expand Down Expand Up @@ -1320,18 +1321,33 @@ exp_list ::=
// FROM [<cube_specification>]
// WHERE [<filter_specification>]
// [<cell_props>]
// |
// [WITH <formula_specification>]
// SELECT [<axis_specification>
// [, <axis_specification>...]]
// FROM ( select_statement )
// WHERE [<filter_specification>]
// [<cell_props>]
// jhyde: The above is wrong... you can omit 'WHERE'.

statement ::=
select_statement
| _VALUE_EXPRESSION value_expression:e {:
RESULT = (ParseTreeNode) e;
:}
;

cube_specification_or_select_statement ::=
cube_specification
| LPAREN select_statement:s RPAREN {:
RESULT = s;
:}
;

select_statement ::=
with_formula_specification_opt:f
SELECT:select axis_specification_list_opt:a
FROM cube_specification:c
FROM cube_specification_or_select_statement:c
where_clause_opt:w
cell_props_opt:cp {:
ParseRegion region = createRegion(selectleft, selectright);
Expand Down
37 changes: 33 additions & 4 deletions testsrc/org/olap4j/ConnectionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1308,10 +1308,8 @@ private void checkUnparsedMdx(
String expectedMdx)
{
StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(pw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(sw);
select.unparse(parseTreeWriter);
pw.flush();
String mdx = sw.toString();
TestContext.assertEqualsVerbose(
expectedMdx, mdx);
Expand All @@ -1324,11 +1322,13 @@ private void checkUnparsedMdx(
public void testUnparsing() {
// Note that the select statement constructed here is equivalent
// to the one in testParsing.
final IdentifierNode cubeName =
new IdentifierNode(new IdentifierNode.NameSegment("sales"));
SelectNode select = new SelectNode(
null,
new ArrayList<ParseTreeNode>(),
new ArrayList<AxisNode>(),
new IdentifierNode(new IdentifierNode.NameSegment("sales")),
cubeName,
new AxisNode(
null,
false,
Expand Down Expand Up @@ -1388,7 +1388,36 @@ public void testUnparsing() {
new IdentifierNode.NameSegment("1997"),
new IdentifierNode.NameSegment("Q4")));

assertEquals(select.getFrom(), cubeName);
checkUnparsedMdx(select);

// Now with a subquery in the FROM clause.
SelectNode subSelect = new SelectNode(
null,
new ArrayList<ParseTreeNode>(),
new ArrayList<AxisNode>(),
new IdentifierNode(new IdentifierNode.NameSegment("warehouse")),
new AxisNode(
null,
false,
Axis.FILTER,
new ArrayList<IdentifierNode>(),
null),
new ArrayList<IdentifierNode>());
select.setFrom(subSelect);

assertEquals(select.getFrom(), subSelect);
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 (\n"
+ " SELECT\n"
+ " FROM [warehouse])\n"
+ "WHERE [Time].[1997].[Q4]");
}

public void testBuildParseTree() {
Expand Down
16 changes: 16 additions & 0 deletions testsrc/org/olap4j/test/ParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,22 @@ public void testEmptyExpr() {
+ "FROM [cube]");
}

/**
* Test case for SELECT in the FROM clause.
*/
public void testInnerSelect() {
assertParseQuery(
"SELECT FROM "
+ "(SELECT ({[ProductDim].[Product Group].&[Mobile Phones]}) "
+ "ON COLUMNS FROM [cube]) CELL PROPERTIES VALUE",
"SELECT\n"
+ "FROM (\n"
+ " SELECT\n"
+ " ({[ProductDim].[Product Group].&[Mobile Phones]}) ON COLUMNS\n"
+ " FROM [cube])\n"
+ "CELL PROPERTIES VALUE");
}

/**
* Parses an MDX query and asserts that the result is as expected when
* unparsed.
Expand Down
7 changes: 4 additions & 3 deletions testsrc/org/olap4j/test/TestContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,16 @@ public static SafeString fold(String string) {
*/
public static String toString(ParseTreeNode node) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(pw);
ParseTreeWriter parseTreeWriter = new ParseTreeWriter(sw);
node.unparse(parseTreeWriter);
pw.flush();
return sw.toString();
}

/**
* Formats a {@link org.olap4j.CellSet}.
*
* @param cellSet Cell set
* @return String representation of cell set
*/
public static String toString(CellSet cellSet) {
StringWriter sw = new StringWriter();
Expand Down

0 comments on commit 09ba75f

Please sign in to comment.