Skip to content

Commit

Permalink
MONDRIAN: Fix bug 1251683 "problem with square bracket in member name".
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//open/mondrian/": change = 4071]
  • Loading branch information
julianhyde committed Sep 9, 2005
1 parent bb703ad commit 8bdb99e
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 35 deletions.
47 changes: 23 additions & 24 deletions src/main/mondrian/olap/Scanner.java
Expand Up @@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.io.IOException;

/**
* Lexical analyzer for MDX.
Expand Down Expand Up @@ -86,8 +87,10 @@ public void init()
advance();
}

/** Deduce the line and column (0-based) of a symbol. Called by {@link
* Parser#syntax_error}. */
/**
* Deduces the line and column (0-based) of a symbol.
* Called by {@link Parser#syntax_error}.
*/
void getLocation(Symbol symbol, int[] loc) {
int iTarget = symbol.left;
int iLine = -1;
Expand Down Expand Up @@ -216,26 +219,31 @@ private Symbol makeNumber(double mantissa, int exponent) {
double d = mantissa * java.lang.Math.pow(10, exponent);
return makeSymbol(ParserSym.NUMBER, new Double(d));
}

private Symbol makeId(String s, boolean quoted, boolean ampersand) {
return makeSymbol(
quoted && ampersand ? ParserSym.AMP_QUOTED_ID :
quoted ? ParserSym.QUOTED_ID :
ParserSym.ID,
s);
}

private Symbol makeRes(int i) {
return makeSymbol(i, m_aResWords[i]);
}

private Symbol makeToken(int i, String s) {
return makeSymbol(i, s);
}

private Symbol makeString( String s ) {
return makeSymbol(ParserSym.STRING, s);
}

/* recognize and return the next complete token */
public Symbol next_token()
throws java.io.IOException {
/**
* Recognizes and returns the next complete token.
*/
public Symbol next_token() throws IOException {

StringBuffer id;
boolean ampersandId = false;
Expand Down Expand Up @@ -350,12 +358,10 @@ public Symbol next_token()
case 'Y': case 'Z':
/* parse an identifier */
id = new StringBuffer();
for (;;)
{
for (;;) {
id.append((char)nextChar);
advance();
switch (nextChar)
{
switch (nextChar) {
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
Expand Down Expand Up @@ -396,16 +402,13 @@ public Symbol next_token()
case '[':
/* parse a delimited identifier */
id = new StringBuffer();
for (;;)
{
for (;;) {
advance();
switch (nextChar)
{
switch (nextChar) {
case ']':
advance();
if (nextChar == ']') {
// ] escaped with ] - leave them as
id.append(']');
// ] escaped with ] - just take one
id.append(']');
break;
} else {
Expand Down Expand Up @@ -463,14 +466,12 @@ public Symbol next_token()
case '"':
/* parse a double-quoted string */
id = new StringBuffer();
for (;;)
{
for (;;) {
advance();
switch (nextChar)
{
switch (nextChar) {
case '"':
advance();
if( nextChar == '"' ){
if (nextChar == '"') {
// " escaped with "
id.append('"');
break;
Expand Down Expand Up @@ -498,11 +499,9 @@ public Symbol next_token()

/* parse a single-quoted string */
id = new StringBuffer();
for (;;)
{
for (;;) {
advance();
switch (nextChar)
{
switch (nextChar) {
case '\'':
advance();
if (nextChar == '\'') {
Expand Down
22 changes: 11 additions & 11 deletions src/main/mondrian/olap/Util.java
Expand Up @@ -221,8 +221,7 @@ public static String[] explode(String s) {
throw getRes().newMdxInvalidMember(s);
}
// s may contain extra ']' characters, so look for a ']' followed
// by a '.' (still not perfect... really ought to scan, ignoring
// escaped ']]' sequences)
// by a '.'
int j = s.indexOf("].", i);
if (j == -1) {
j = s.lastIndexOf(']');
Expand All @@ -231,30 +230,31 @@ public static String[] explode(String s) {
throw getRes().newMdxInvalidMember(s);
}
String sub = s.substring(i + 1, j);
sub = replace(sub, "]]", "]");
list.add(sub);
if (j + 1 < s.length())
if (s.charAt(j+1) != '.') {
if (j + 1 < s.length()) {
if (s.charAt(j + 1) != '.') {
throw getRes().newMdxInvalidMember(s);
}
}
i = j + 2;
}
String[] names = (String[]) list.toArray(new String[list.size()]);
return names;
}

/**
* Converts an array of name parts {"part1", "part2"} into a single string
* "[part1].[part2]". If the names contain "]" they are escaped as "]]".
*/
public static String implode(String[] names) {
if (names.length == 0) {
return "";
}
StringBuffer sb = new StringBuffer(64);
sb.append('[');
for (int i = 0; i < names.length; i++) {
if (i > 0) {
sb.append("].[");
sb.append(".");
}
sb.append(names[i]);
quoteMdxIdentifier(names[i], sb);
}
sb.append(']');
return sb.toString();
}

Expand Down
19 changes: 19 additions & 0 deletions testsrc/main/mondrian/olap/UtilTestCase.java
Expand Up @@ -239,6 +239,25 @@ private static void checkReplace(
// Check the String version of replace.
assertEquals(expected, Util.replace(original, seek, replace));
}

public void testImplode() {
String[] fooBar = {"foo", "bar"};
assertEquals("[foo].[bar]", Util.implode(fooBar));

String[] empty = {};
assertEquals("", Util.implode(empty));

String[] nasty = {"string", "with", "a [bracket] in it"};
assertEquals("[string].[with].[a [bracket]] in it]",
Util.implode(nasty));
}

public void testExplode() {
String[] strings =
Util.explode("[string].[with].[a [bracket]] in it]");
assertEquals(3, strings.length);
assertEquals("a [bracket] in it", strings[2]);
}
}

// End UtilTestCase.java
66 changes: 66 additions & 0 deletions testsrc/main/mondrian/test/TestCalculatedMembers.java
Expand Up @@ -340,6 +340,72 @@ public void testCalcMemberIsSetFails() {
"Member expression '{[Product].[All Products].[Food]}' must not be a set");
}

/**
* Tests that calculated members can have brackets in their names.
* (Bug 1251683.)
*/
public void testBracketInCalcMemberName() {
assertQueryReturns(
fold(new String[] {
"with member [Measures].[has a [bracket]] in it] as ",
"' [Measures].CurrentMember.Name '",
"select {[Measures].[has a [bracket]] in it]} on columns",
"from [Sales]"}),
"Axis #0:" + nl +
"{}" + nl +
"Axis #1:" + nl +
"{[Measures].[has a [bracket]] in it]}" + nl +
"Row #0: Unit Sales" + nl);
}

/**
* Tests that calculated members defined in the schema can have brackets in
* their names. (Bug 1251683.)
*/
public void testBracketInCubeCalcMemberName() {
Schema schema = getConnection().getSchema();
final String cubeName = "Sales_BracketInCubeCalcMemberName";
schema.createCube(fold(new String[] {
"<Cube name=\"" + cubeName + "\">",
" <Table name=\"sales_fact_1997\"/>",
" <Dimension name=\"Gender\" foreignKey=\"customer_id\">",
" <Hierarchy hasAll=\"false\" primaryKey=\"customer_id\">",
" <Table name=\"customer\"/>",
" <Level name=\"Gender\" column=\"gender\" uniqueMembers=\"true\"/>",
" </Hierarchy>",
" </Dimension>",
" <Measure name=\"Unit Sales\" column=\"unit_sales\" aggregator=\"sum\"",
" formatString=\"Standard\" visible=\"false\"/>",
" <CalculatedMember",
" name=\"With a [bracket] in it\"",
" dimension=\"Measures\"",
" visible=\"false\"",
" formula=\"[Measures].[Unit Sales] * 10\">",
" <CalculatedMemberProperty name=\"FORMAT_STRING\" value=\"$#,##0.00\"/>",
" </CalculatedMember>",
"</Cube>"}));

getTestContext(cubeName).assertThrows(fold(new String[] {
"select {[Measures].[With a [bracket] in it]} on columns,",
" {[Gender].Members} on rows",
"from [" + cubeName + "]"}),
"Syntax error at line 1, column 38, token 'in'");

getTestContext(cubeName).assertQueryReturns(fold(new String[] {
"select {[Measures].[With a [bracket]] in it]} on columns,",
" {[Gender].Members} on rows",
"from [" + cubeName + "]"}),
fold(new String[] {
"Axis #0:",
"{}",
"Axis #1:",
"{[Measures].[With a [bracket]] in it]}",
"Axis #2:",
"{[Gender].[F]}",
"{[Gender].[M]}",
"Row #0: $1,315,580.00",
"Row #1: $1,352,150.00"}));
}
}

// End CalculatedMembersTestCase.java

0 comments on commit 8bdb99e

Please sign in to comment.