Skip to content

Commit

Permalink
MONDRIAN: Fix bug 1801218, "XMLA execute + tabular format". Fix contr…
Browse files Browse the repository at this point in the history
…ibuted by jcaubin (Julio Caubín).

    Allow member and tuple expressions on axes.

[git-p4: depot-paths = "//open/mondrian/": change = 9907]
  • Loading branch information
julianhyde committed Sep 24, 2007
1 parent e47308e commit 61e9983
Show file tree
Hide file tree
Showing 8 changed files with 974 additions and 77 deletions.
22 changes: 16 additions & 6 deletions src/main/mondrian/olap/QueryAxis.java
Expand Up @@ -13,12 +13,9 @@

package mondrian.olap;

import mondrian.calc.Calc;
import mondrian.calc.ExpCompiler;
import mondrian.calc.ResultStyle;
import mondrian.calc.*;
import mondrian.mdx.*;
import mondrian.olap.type.Type;
import mondrian.olap.type.TypeUtil;
import mondrian.olap.type.*;
import mondrian.resource.MondrianResource;

import java.io.PrintWriter;
Expand Down Expand Up @@ -201,7 +198,20 @@ public void resolve(Validator validator) {
exp = validator.validate(exp, false);
final Type type = exp.getType();
if (!TypeUtil.isSet(type)) {
throw MondrianResource.instance().MdxAxisIsNotSet.ex(axisOrdinal.name());
// If expression is a member or a tuple, implicitly convert it
// into a set.
if (type instanceof MemberType ||
type instanceof TupleType) {
exp =
new UnresolvedFunCall(
"{}",
Syntax.Braces,
new Exp[] {exp});
exp = validator.validate(exp, false);
} else {
throw MondrianResource.instance().MdxAxisIsNotSet.ex(
axisOrdinal.name());
}
}
}

Expand Down
157 changes: 92 additions & 65 deletions src/main/mondrian/xmla/XmlaHandler.java
Expand Up @@ -81,13 +81,13 @@ public class XmlaHandler implements XmlaConstants {
public static final long XSD_LONG_MAX_INCLUSIVE = 9223372036854775807L;
public static final long XSD_LONG_MIN_INCLUSIVE = -9223372036854775808L;

// xsd:double IEEE 64-bit floating-point
// xsd:double: IEEE 64-bit floating-point
public static final String XSD_DOUBLE = "xsd:double";

// xsd:decimal Decimal numbers (BigDecimal)
// xsd:decimal: Decimal numbers (BigDecimal)
public static final String XSD_DECIMAL = "xsd:decimal";

// xsd:integer Signed integers of arbitrary length (BigInteger)
// xsd:integer: Signed integers of arbitrary length (BigInteger)
public static final String XSD_INTEGER = "xsd:integer";

public static boolean isValidXsdInt(long l) {
Expand Down Expand Up @@ -181,9 +181,8 @@ static String getValueTypeHint(final String dataType) {
this.isDecimal = false;

} else if (inputValue instanceof Long) {
Long l = (Long) inputValue;
// See if it can be an integer or long
long lval = l.longValue();
long lval = (Long) inputValue;
setValueAndType(lval);

} else if (inputValue instanceof BigInteger) {
Expand Down Expand Up @@ -267,8 +266,7 @@ static String getValueTypeHint(final String dataType) {
} else if (inputValue instanceof Number) {
// Don't know what Number type we have here.
// Note: this could result in precision loss.
long lval = ((Number) inputValue).longValue();
this.value = new Long(lval);
this.value = ((Number) inputValue).longValue();
this.valueType = valueTypeHint;
this.isDecimal = false;

Expand Down Expand Up @@ -296,8 +294,7 @@ static String getValueTypeHint(final String dataType) {
inputValue instanceof Integer ||
inputValue instanceof Long) {
// Convert from byte/short/integer/long to double
double dval = ((Number) inputValue).doubleValue();
this.value = new Double(dval);
this.value = ((Number) inputValue).doubleValue();
this.valueType = valueTypeHint;
this.isDecimal = true;

Expand All @@ -317,7 +314,7 @@ static String getValueTypeHint(final String dataType) {
// Must use compareTo - see BigDecimal.equals
if (bd.compareTo(bd2) == 0) {
this.valueType = XSD_DOUBLE;
this.value = new Double(dval);
this.value = dval;
} else {
this.valueType = XSD_DECIMAL;
this.value = inputValue;
Expand Down Expand Up @@ -351,8 +348,7 @@ static String getValueTypeHint(final String dataType) {
} else if (inputValue instanceof Number) {
// Don't know what Number type we have here.
// Note: this could result in precision loss.
double dval = ((Number) inputValue).doubleValue();
this.value = new Double(dval);
this.value = ((Number) inputValue).doubleValue();
this.valueType = valueTypeHint;
this.isDecimal = true;

Expand All @@ -379,20 +375,18 @@ static String getValueTypeHint(final String dataType) {
} else if (inputValue instanceof Byte) {
Byte b = (Byte) inputValue;
this.valueType = XSD_INT;
this.value = new Integer(b.intValue());
this.value = b.intValue();
this.isDecimal = false;

} else if (inputValue instanceof Short) {
Short s = (Short) inputValue;
this.valueType = XSD_INT;
this.value = new Integer(s.intValue());
this.value = s.intValue();
this.isDecimal = false;

} else if (inputValue instanceof Long) {
Long l = (Long) inputValue;
// See if it can be an integer or long
long lval = l.longValue();
setValueAndType(lval);
setValueAndType((Long) inputValue);

} else if (inputValue instanceof BigInteger) {
BigInteger bi = (BigInteger) inputValue;
Expand Down Expand Up @@ -431,7 +425,7 @@ static String getValueTypeHint(final String dataType) {
// Must use compareTo - see BigDecimal.equals
if (bd.compareTo(bd2) == 0) {
this.valueType = XSD_DOUBLE;
this.value = new Double(dval);
this.value = dval;
} else {
this.valueType = XSD_DECIMAL;
this.value = inputValue;
Expand All @@ -445,8 +439,7 @@ static String getValueTypeHint(final String dataType) {
} else if (inputValue instanceof Number) {
// Don't know what Number type we have here.
// Note: this could result in precision loss.
long lval = ((Number) inputValue).longValue();
this.value = new Long(lval);
this.value = ((Number) inputValue).longValue();
this.valueType = XSD_LONG;
this.isDecimal = false;

Expand All @@ -463,11 +456,11 @@ private void setValueAndType(long lval) {
if (! isValidXsdInt(lval)) {
// No, it can not be a integer, must be a long
this.valueType = XSD_LONG;
this.value = new Long(lval);
this.value = lval;
} else {
// Its an integer.
this.valueType = XSD_INT;
this.value = new Integer((int) lval);
this.value = (int) lval;
}
this.isDecimal = false;
}
Expand Down Expand Up @@ -540,6 +533,7 @@ public Map<String, DataSourcesConfig.DataSource> getDataSourceEntries() {
*
* @param request XML request, for example, "<SOAP-ENV:Envelope ...>".
* @param response Destination for response
* @throws XmlaException on error
*/
public void process(XmlaRequest request, XmlaResponse response)
throws XmlaException {
Expand Down Expand Up @@ -708,6 +702,7 @@ private void execute(XmlaRequest request, XmlaResponse response)
* Computes the XML Schema for a dataset.
*
* @param writer SAX writer
* @param settype rowset or dataset?
* @see RowsetDefinition#writeRowsetXmlSchema(SaxWriter)
*/
static void writeDatasetXmlSchema(SaxWriter writer, int settype) {
Expand Down Expand Up @@ -2308,17 +2303,16 @@ public MDDataSet_Tabular(Result result) {
final int z0 = memberOrdinal; // save ordinal so can rewind
final List<Position> positions = axis.getPositions();
int jj = 0;
for (Position position: positions) {
for (Position position : positions) {
memberOrdinal = z0; // rewind to start
//final Member[] members = position.members;
for (Member member : position) {
if (jj == 0 ||
member.getLevel().getDepth() >
levels[memberOrdinal].getDepth()) {
levels[memberOrdinal] = member.getLevel();
}
memberOrdinal++;
}
++memberOrdinal;
jj++;
}

Expand Down Expand Up @@ -2347,23 +2341,25 @@ public MDDataSet_Tabular(Result result) {
}
}
}
this.members = new Member[memberOrdinal];
this.members = new Member[memberOrdinal + 1];

// Deduce the list of column headings.
Axis columnsAxis = axes[0];
for (Position position : columnsAxis.getPositions()) {
String name = null;
int j = 0;
for (Member member : position) {
if (j == 0) {
name = member.getUniqueName();
} else {
name = name + "." + member.getUniqueName();
if (axes.length > 0) {
Axis columnsAxis = axes[0];
for (Position position : columnsAxis.getPositions()) {
String name = null;
int j = 0;
for (Member member : position) {
if (j == 0) {
name = member.getUniqueName();
} else {
name = name + "." + member.getUniqueName();
}
j++;
}
j++;
columnHandlerList.add(
new CellColumnHandler(name));
}
columnHandlerList.add(
new CellColumnHandler(name));
}

this.columnHandlers =
Expand All @@ -2372,9 +2368,11 @@ public MDDataSet_Tabular(Result result) {
}

public void metadata(SaxWriter writer) {
if (empty) {
return;
}
// ADOMD wants a XSD even a void one.
// if (empty) {
// return;
// }

writer.startElement("xsd:schema", new String[] {
"xmlns:xsd", XmlaConstants.NS_XSD,
"targetNamespace", NS_XMLA_ROWSET,
Expand Down Expand Up @@ -2438,34 +2436,63 @@ public void unparse(SaxWriter writer) throws SAXException {

private void cellData(SaxWriter writer) throws SAXException {
cellOrdinal = 0;
if (axisCount == 0) { // For MDX like: SELECT FROM Sales
emitCell(writer, result.getCell(pos));
} else {
recurse(writer, axisCount - 1, 0);
}
iterate(writer);
}

private void recurse(
SaxWriter writer,
int axis,
final int headerOrdinal) throws SAXException {
final List<Position> positions = result.getAxes()[axis].getPositions();
int i = 0;
for (Position position: positions) {
pos[axis] = i;
if (axis == 0) {
final Cell cell = result.getCell(pos);
emitCell(writer, cell);
/**
* Iterates over the resust writing tabular rows.
*
* @param writer Writer
* @throws org.xml.sax.SAXException on error
*/
private void iterate(SaxWriter writer) throws SAXException {
switch (axisCount) {
case 0:
// For MDX like: SELECT FROM Sales
emitCell(writer, result.getCell(pos));
return;
default:
// throw new SAXException("Too many axes: " + axisCount);
iterate(writer, axisCount - 1, 0);
break;
}
}

private void iterate(SaxWriter writer, int axis, final int xxx) {
final List<Position> positions =
result.getAxes()[axis].getPositions();
int axisLength = axis == 0 ? 1 : positions.size();

for (int i = 0; i < axisLength; i++) {
final Position position = positions.get(i);
int ho = xxx;
for (int j = 0;
j < position.size() && ho < members.length;
j++, ho++)
{
members[ho] = position.get(j);
}

++cellOrdinal;
Util.discard(cellOrdinal);

if (axis >= 2) {
iterate(writer, axis - 1, ho);
} else {
// Populate headers and values with levels.
int ho = headerOrdinal;
for (Member member : position) {
members[ho++] = member;
}

recurse(writer, axis - 1, ho);
writer.startElement("row");//abrimos la fila
pos[axis] = i; //coordenadas: fila i
pos[0] = 0; //coordenadas (0,i): columna 0
for (ColumnHandler columnHandler : columnHandlers) {
if (columnHandler instanceof MemberColumnHandler) {
columnHandler.write(writer, null, members);
} else if (columnHandler instanceof CellColumnHandler) {
columnHandler.write(writer, result.getCell(pos), null);
pos[0]++;// next col.
}
}
writer.endElement();//cerramos la fila
}
i++;
}
}

Expand Down Expand Up @@ -2824,7 +2851,7 @@ private TabularRowSet executeColumnQuery(XmlaRequest request)
buf.append(whereClause);
dtSql = buf.toString();

DataSource dataSource = ((RolapConnection) connection).getDataSource();
DataSource dataSource = connection.getDataSource();
try {
int count = -1;
if (MondrianProperties.instance().EnableTotalCount.booleanValue()) {
Expand Down
12 changes: 12 additions & 0 deletions testsrc/main/mondrian/olap/ParserTest.java
Expand Up @@ -176,6 +176,18 @@ public void testMultipleAxes() throws Exception {
assertEquals("Correct member on axis", "axis1mbr", id.name);
}

/**
* If an axis expression is a member, implicitly convert it to a set.
*/
public void testMemberOnAxis() {
assertParseQuery(
"select [Measures].[Sales Count] on 0, non empty [Store].[Store State].members on 1 from [Sales]",
TestContext.fold(
"select [Measures].[Sales Count] ON COLUMNS,\n" +
" NON EMPTY [Store].[Store State].members ON ROWS\n" +
"from [Sales]\n"));
}

public void testCaseTest() {
assertParseQuery(
"with member [Measures].[Foo] as " +
Expand Down
25 changes: 25 additions & 0 deletions testsrc/main/mondrian/test/BasicQueryTest.java
Expand Up @@ -4962,6 +4962,31 @@ public String getDefaultCubeName() {
testContext.assertExprReturns("([Product].[Drink].[Dairy], [Measures].[Unit Sales])", "1,987");
}

/**
* If an axis expression is a member, implicitly convert it to a set.
*/
public void testMemberOnAxis() {
assertQueryReturns(
"select [Measures].[Sales Count] on 0, non empty [Store].[Store State].members on 1 from [Sales]",
fold("Axis #0:\n" +
"{}\n" +
"Axis #1:\n" +
"{[Measures].[Sales Count]}\n" +
"Axis #2:\n" +
"{[Store].[All Stores].[USA].[CA]}\n" +
"{[Store].[All Stores].[USA].[OR]}\n" +
"{[Store].[All Stores].[USA].[WA]}\n" +
"Row #0: 24,442\n" +
"Row #1: 21,611\n" +
"Row #2: 40,784\n"));
}

public void testScalarOnAxisFails() {
assertThrows(
"select [Measures].[Sales Count] + 1 on 0, non empty [Store].[Store State].members on 1 from [Sales]",
"Axis 'COLUMNS' expression is not a set");
}

/**
* It is illegal for a query to have the same dimension on more than
* one axis.
Expand Down

0 comments on commit 61e9983

Please sign in to comment.