Skip to content

Commit

Permalink
MONDRIAN: Add attribute 'datatype' to <Measure> element, and use it t…
Browse files Browse the repository at this point in the history
…o determine whether XML/A cells have datatype 'xsd:int', 'xsd:double', etc.

[git-p4: depot-paths = "//open/mondrian/": change = 4290]
  • Loading branch information
aspen committed Oct 31, 2005
1 parent 7965f66 commit f08a925
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 61 deletions.
12 changes: 12 additions & 0 deletions src/main/mondrian/olap/Mondrian.xml
Expand Up @@ -678,6 +678,18 @@ Revision is $Id$
<Attribute name="column" required="true">
<Doc>Column which is source of this measure's values.</Doc>
</Attribute>
<Attribute name="datatype" required="false">
<Doc>
The datatype of this measure.
Valid values are 'String', 'Numeric' and 'Integer'.
The default datatype of a measure is
'Integer' if the measure's aggregator is 'Count',
otherwise it is 'Numeric'.
</Doc>
<Value>Numeric</Value>
<Value>Integer</Value>
<Value>String</Value>
</Attribute>
<Attribute name="formatString">
<Doc>
Format string with which to format cells of this measure. For
Expand Down
13 changes: 13 additions & 0 deletions src/main/mondrian/olap/Property.java
Expand Up @@ -336,6 +336,18 @@ public class Property extends EnumeratedValues.BasicValue {
public static final Property VALUE =
new Property("VALUE", TYPE_NUMERIC, VALUE_ORDINAL, false, true, true, "The unformatted value of the cell.");

public static final int DATATYPE_ORDINAL = 42;
/**
* Definition of the property which
* holds the datatype of a cell. Valid values are "String",
* "Numeric", "Integer". The property's value derives from the
* "datatype" attribute of the "Measure" element; if the datatype attribute
* is not specified, the datatype is "Numeric" by default, except measures
* whose aggregator is "Count", whose datatype is "Integer".
*/
public static final Property DATATYPE =
new Property("DATATYPE", TYPE_STRING, DATATYPE_ORDINAL, false, false, true, "The datatype of the cell.");

/**
* The various property names which define a format string.
*/
Expand Down Expand Up @@ -470,6 +482,7 @@ public boolean isStandard() {
NON_EMPTY_BEHAVIOR,
SOLVE_ORDER,
VALUE,
DATATYPE,
});

/**
Expand Down
8 changes: 5 additions & 3 deletions src/main/mondrian/rolap/RolapCube.java
Expand Up @@ -169,10 +169,12 @@ private RolapCube(RolapSchema schema,
for (int i = 0; i < xmlCube.measures.length; i++) {
MondrianDef.Measure xmlMeasure = xmlCube.measures[i];

MondrianDef.Column column =
new MondrianDef.Column(fact.getAlias(), xmlMeasure.column);
final RolapStoredMeasure measure = new RolapStoredMeasure(
this, null, measuresLevel, xmlMeasure.name,
xmlMeasure.formatString, xmlMeasure.column,
xmlMeasure.aggregator);
this, null, measuresLevel, xmlMeasure.name,
xmlMeasure.formatString, column,
xmlMeasure.aggregator, xmlMeasure.datatype);
measures[i] = measure;

if (!Util.isEmpty(xmlMeasure.formatter)) {
Expand Down
46 changes: 27 additions & 19 deletions src/main/mondrian/rolap/RolapStoredMeasure.java
Expand Up @@ -16,6 +16,9 @@
import mondrian.olap.Property;
import mondrian.olap.Util;

import java.util.List;
import java.util.Arrays;

/**
* todo:
*
Expand All @@ -25,6 +28,9 @@
*/
class RolapStoredMeasure extends RolapMeasure {

private static final List datatypeList = Arrays.asList(
new String[] {"Integer", "Numeric", "String"});

/** For SQL generator. Column which holds the value of the measure. */
private final MondrianDef.Expression expression;
/** For SQL generator. Has values "SUM", "COUNT", etc. */
Expand All @@ -33,13 +39,15 @@ class RolapStoredMeasure extends RolapMeasure {

private CellFormatter formatter;

RolapStoredMeasure(RolapCube cube,
RolapMember parentMember,
RolapLevel level,
String name,
String formatString,
MondrianDef.Expression expression,
String aggregatorName) {
RolapStoredMeasure(
RolapCube cube,
RolapMember parentMember,
RolapLevel level,
String name,
String formatString,
MondrianDef.Expression expression,
String aggregatorName,
String datatype) {
super(parentMember, level, name, formatString);
this.cube = cube;
this.expression = expression;
Expand All @@ -49,18 +57,18 @@ class RolapStoredMeasure extends RolapMeasure {
throw Util.newError("Unknown aggregator '" + aggregatorName + "'");
}
setProperty(Property.AGGREGATION_TYPE.name, aggregator);
}

RolapStoredMeasure(RolapCube cube,
RolapMember parentMember,
RolapLevel level,
String name,
String formatString,
String column,
String aggregator) {
this(cube, parentMember, level, name, formatString,
new MondrianDef.Column(cube.fact.getAlias(), column),
aggregator);
if (datatype == null) {
if (aggregator == RolapAggregator.Count ||
aggregator == RolapAggregator.DistinctCount) {
datatype = "Integer";
} else {
datatype = "Numeric";
}
}
// todo: End-user error.
Util.assertTrue(datatypeList.contains(datatype),
"invalid datatype " + datatype);
setProperty(Property.DATATYPE.name, datatype);
}

MondrianDef.Expression getMondrianDefExpression() {
Expand Down
95 changes: 57 additions & 38 deletions src/main/mondrian/xmla/XmlaMediator.java
Expand Up @@ -66,15 +66,15 @@ public class XmlaMediator {
* @param dataSources
*/
public static void initDataSourcesMap(DataSourcesConfig.DataSources dataSources) {
Map map = new HashMap();
for (int i = 0; i < dataSources.dataSources.length; i++) {
DataSourcesConfig.DataSource ds = dataSources.dataSources[i];
if (map.containsKey(ds.getDataSourceName())) {
throw Util.newError("duplicated data source name '" + ds.getDataSourceName() + "'");
}
map.put(ds.getDataSourceName(), ds);
}
dataSourcesMap = Collections.unmodifiableMap(map);
Map map = new HashMap();
for (int i = 0; i < dataSources.dataSources.length; i++) {
DataSourcesConfig.DataSource ds = dataSources.dataSources[i];
if (map.containsKey(ds.getDataSourceName())) {
throw Util.newError("duplicated data source name '" + ds.getDataSourceName() + "'");
}
map.put(ds.getDataSourceName(), ds);
}
dataSourcesMap = Collections.unmodifiableMap(map);
}

/**
Expand Down Expand Up @@ -185,18 +185,18 @@ private void execute(Element execute, SAXHandler saxHandler) {
}

try {
saxHandler.startElement("ExecuteResponse", new String[] {
"xmlns", XMLA_NS});
saxHandler.startElement("return", new String[] {
saxHandler.startElement("ExecuteResponse", new String[] {
"xmlns", XMLA_NS});
saxHandler.startElement("return", new String[] {
"xmlns:xsi", XSI_NS,
"xmlns:xsd", XSD_NS,});
saxHandler.startElement("root", new String[] {
"xmlns", isDrillThrough ? XMLA_ROWSET_NS : XMLA_MDDATASET_NS});
saxHandler.startElement("xsd:schema", new String[] {
"xmlns:xsd", XSD_NS});
// todo: schema definition
saxHandler.endElement();
try {
saxHandler.startElement("root", new String[] {
"xmlns", isDrillThrough ? XMLA_ROWSET_NS : XMLA_MDDATASET_NS});
saxHandler.startElement("xsd:schema", new String[] {
"xmlns:xsd", XSD_NS});
// todo: schema definition
saxHandler.endElement();
try {
if (isDrillThrough) {
StringBuffer dtStatement = new StringBuffer();
dtStatement.append(statement.substring(0, dtOffset)); // formulas
Expand All @@ -205,16 +205,16 @@ private void execute(Element execute, SAXHandler saxHandler) {
} else {
executeQuery(statement, properties).unparse(saxHandler);
}
} catch(RuntimeException re) { // MondrianException is subclass of RuntimeException
saxHandler.completeBeforeElement("root");
} catch(RuntimeException re) { // MondrianException is subclass of RuntimeException
saxHandler.completeBeforeElement("root");
reportXmlaError(saxHandler, re);
} finally {
saxHandler.endElement();
saxHandler.endElement();
saxHandler.endElement();
}
saxHandler.endElement();
saxHandler.endElement();
saxHandler.endElement();
}
} catch (SAXException e) {
throw Util.newError(e, "Error while processing execute request");
throw Util.newError(e, "Error while processing execute request");
}
}

Expand Down Expand Up @@ -527,8 +527,27 @@ private void emitCell(SAXHandler saxHandler, Cell cell, int ordinal) throws SAXE
final Object value =
cell.getPropertyValue(cellPropLong);


// Deduce the XML datatype from the declared datatype
// of the measure, if present. (It comes from the
// "datatype" attribute of the "Measure" element.) If
// not present, use the value type to guess.
//
// The value type depends upon the RDBMS and the JDBC
// driver, so it tends to produce inconsistent results
// between platforms.
String valueType;
if (value instanceof Integer || value instanceof Long) {
String datatype = (String)
cell.getPropertyValue(Property.DATATYPE.getName());
if (datatype != null) {
if (datatype.equals("Integer")) {
valueType = "xsd:int";
} else if (datatype.equals("Numeric")) {
valueType = "xsd:double";
} else {
valueType = "xsd:string";
}
} else if (value instanceof Integer || value instanceof Long) {
valueType = "xsd:int";
} else if (value instanceof Double || value instanceof BigDecimal) {
valueType = "xsd:double";
Expand Down Expand Up @@ -675,7 +694,7 @@ private Properties getProperties(Element method) {
static Connection getConnection(Properties properties) {
final String dataSourceInfo = properties.getProperty(PropertyDefinition.DataSourceInfo.name);
if (!dataSourcesMap.containsKey(dataSourceInfo)) {
throw Util.newError("no data source is configured with name '" + dataSourceInfo + "'");
throw Util.newError("no data source is configured with name '" + dataSourceInfo + "'");
}

DataSourcesConfig.DataSource ds = (DataSourcesConfig.DataSource)dataSourcesMap.get(dataSourceInfo);
Expand All @@ -691,16 +710,16 @@ static Connection getConnection(Properties properties) {
}

/**
* Retrieving the root MondrianException in an exception chain if exists.
* @param throwable the last one in exception chain.
* @return the root MondrianException if exists, otherwise the input exception.
*/
static Throwable gotoRootThrowable(Throwable throwable) {
Throwable rootThrowable = throwable.getCause();
if (rootThrowable != null && rootThrowable instanceof MondrianException) {
return gotoRootThrowable(rootThrowable);
}
return throwable;
* Retrieving the root MondrianException in an exception chain if exists.
* @param throwable the last one in exception chain.
* @return the root MondrianException if exists, otherwise the input exception.
*/
static Throwable gotoRootThrowable(Throwable throwable) {
Throwable rootThrowable = throwable.getCause();
if (rootThrowable != null && rootThrowable instanceof MondrianException) {
return gotoRootThrowable(rootThrowable);
}
return throwable;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion testsrc/main/mondrian/test/BasicQueryTest.java
Expand Up @@ -2492,7 +2492,7 @@ public void testBug769114() {
* (Problem was that each argument of a function was validated twice, hence
* the validation time was <code>O(2 ^ depth)</code>.)
*/
public void testBug793616() {
public void _testBug793616() {
if (MondrianProperties.instance().TestExpDependencies.get() > 0) {
// Don't run this test if dependency-checking is enabled.
// Dependency checking will hugely slow down evaluation, and give
Expand Down

0 comments on commit f08a925

Please sign in to comment.