Skip to content

Commit

Permalink
MONDRIAN: Add MEMBER_ORDINAL property, so that order of stored and ca…
Browse files Browse the repository at this point in the history
…lculated members can be controlled (bug 1465731, fix due to rehdie_easyplex);

    Scanner now complains if it sees a character it is not expecting (previously it just ignored it);
    When sorting members by name, observe the case-sensitivity preference.

[git-p4: depot-paths = "//open/mondrian/": change = 6714]
  • Loading branch information
julianhyde committed May 27, 2006
1 parent 51c172a commit 1a6fc9a
Show file tree
Hide file tree
Showing 22 changed files with 1,587 additions and 64 deletions.
51 changes: 51 additions & 0 deletions demo/FoodMart.xml
Expand Up @@ -619,6 +619,57 @@ fullname
formatString="#,###"/>
</Cube>

<!-- a simpler version of "Sales" (with MEMBER_ORDINAL-properties) -->
<Cube name="Sales 2">
<Table name="sales_fact_1997"/>

<DimensionUsage name="Time" source="Time" foreignKey="time_id"/>
<DimensionUsage name="Product" source="Product" foreignKey="product_id"/>

<Dimension name="Gender" foreignKey="customer_id">
<Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="customer_id">
<Table name="customer"/>
<Level name="Gender" column="gender" uniqueMembers="true"/>
</Hierarchy>
</Dimension>

<Measure name="Sales Count" column="product_id" aggregator="count" formatString="#,###">
<CalculatedMemberProperty name="MEMBER_ORDINAL" value="1"/>
</Measure>

<Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="Standard">
<CalculatedMemberProperty name="MEMBER_ORDINAL" value="2"/>
</Measure>

<Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.00">
<CalculatedMemberProperty name="MEMBER_ORDINAL" value="3"/>
</Measure>

<Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00">
<CalculatedMemberProperty name="MEMBER_ORDINAL" value="6"/>
</Measure>

<Measure name="Customer Count" column="customer_id" aggregator="distinct count" formatString="#,###">
<CalculatedMemberProperty name="MEMBER_ORDINAL" value="7"/>
</Measure>

<CalculatedMember
name="Profit"
dimension="Measures">
<Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
<CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
<CalculatedMemberProperty name="MEMBER_ORDINAL" value="4"/>
</CalculatedMember>

<CalculatedMember
name="Profit last Period"
dimension="Measures"
formula="COALESCEEMPTY((Measures.[Profit], [Time].PREVMEMBER), Measures.[Profit])"
visible="false">
<CalculatedMemberProperty name="MEMBER_ORDINAL" value="5"/>
</CalculatedMember>
</Cube>

<VirtualCube name="Warehouse and Sales">
<VirtualCubeDimension cubeName="Sales" name="Customers"/>
<VirtualCubeDimension cubeName="Sales" name="Education Level"/>
Expand Down
20 changes: 19 additions & 1 deletion src/main/mondrian/olap/Formula.java
Expand Up @@ -17,6 +17,8 @@
import mondrian.mdx.MemberExpr;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;

/**
* A <code>Formula</code> is a clause in an MDX query which defines a Set or a
Expand Down Expand Up @@ -119,6 +121,22 @@ void accept(Validator validator) {
if (formatExp != null) {
mdxMember.setProperty(Property.FORMAT_EXP.name, formatExp);
}

// For each property of the formula, make it a property of the
// member.
final List formatPropertyList =
Arrays.asList(Property.FORMAT_PROPERTIES);
for (int i = 0; i < memberProperties.length; i++) {
MemberProperty memberProperty = memberProperties[i];
if (formatPropertyList.contains(memberProperty.getName())) {
continue; // we already dealt with format_string props
}
final Exp exp = memberProperty.getExp();
if (exp instanceof Literal) {
String value = String.valueOf(((Literal) exp).getValue());
mdxMember.setProperty(memberProperty.getName(), value);
}
}
}
}

Expand Down Expand Up @@ -192,7 +210,7 @@ public void unparse(PrintWriter pw)
if (mdxMember != null) {
pw.print(mdxMember.getUniqueName());
} else {
pw.print(Util.quoteMdxIdentifier(names));
pw.print(Util.quoteMdxIdentifier(names));
}
} else {
pw.print("set ");
Expand Down
8 changes: 8 additions & 0 deletions src/main/mondrian/olap/MemberProperty.java
Expand Up @@ -43,6 +43,14 @@ void resolve(Validator validator) {
exp = validator.validate(exp, false);
}

public Exp getExp() {
return exp;
}

public String getName() {
return name;
}

public Object[] getChildren() {
return new Exp[] {exp};
}
Expand Down
16 changes: 13 additions & 3 deletions src/main/mondrian/olap/Scanner.java
Expand Up @@ -692,8 +692,20 @@ public Symbol next_token() throws IOException {
case -1:
// we're done
return makeToken(ParserSym.EOF, "EOF");

default:
/* ignore everything else */
// everything else is an error
if (Character.isWhitespace(nextChar)) {
// fall through
} else {
throw new RuntimeException("Unexpected character '" + (char) nextChar + "'");
}

case ' ':
case '\t':
case '\n':
case '\r':
// whitespace can be ignored
iPrevChar = iChar;
advance();
break;
Expand All @@ -702,6 +714,4 @@ public Symbol next_token() throws IOException {
}
}



// End Scanner.java
11 changes: 11 additions & 0 deletions src/main/mondrian/olap/Util.java
Expand Up @@ -250,6 +250,17 @@ public static boolean equalName(String s, String t) {
return caseSensitive ? s.equals(t) : s.equalsIgnoreCase(t);
}

/**
* Compares two names.
* Takes into account the {@link MondrianProperties#CaseSensitive case
* sensitive option}.
* Names must not be null.
*/
public static int compareName(String s, String t) {
boolean caseSensitive = MondrianProperties.instance().CaseSensitive.get();
return caseSensitive ? s.compareTo(t) : s.compareToIgnoreCase(t);
}

/**
* Generates a normalized form of a name, for use as a key into a map.
* Returns the upper case name if
Expand Down
5 changes: 1 addition & 4 deletions src/main/mondrian/olap/fun/BuiltinFunTable.java
Expand Up @@ -2126,10 +2126,7 @@ static List dimensionMembers(
if (!includeCalcMembers && memberList != null) {
FunUtil.removeCalculatedMembers(memberList);
}
if (!dimension.isMeasures()) {
// leave measures in their natural order (calculated members last)
FunUtil.hierarchize(memberList, false);
}
FunUtil.hierarchize(memberList, false);
return memberList;
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/mondrian/resource/MondrianResource.xml
Expand Up @@ -425,6 +425,9 @@
<text>Named set ''{0}'' has bad formula</text>
</exception>

<exception id="40250" name="MeasureOrdinalsNotUnique">
<text>Cube ''{0}'': Ordinal {1} is not unique: ''{2}'' and ''{3}''</text>
</exception>

<!-- ====================================================================== -->
<!-- Loader -->
Expand Down
89 changes: 80 additions & 9 deletions src/main/mondrian/rolap/RolapCube.java
Expand Up @@ -222,6 +222,7 @@ private RolapCube(RolapSchema schema,
init(xmlCube.dimensions);
init(xmlCube);

checkOrdinals(xmlCube.name, measures, xmlCube.calculatedMembers);
loadAggGroup(xmlCube);
}

Expand Down Expand Up @@ -373,6 +374,65 @@ private void init(MondrianDef.Cube xmlCube) {
memberList, formulaList);
}


/**
* Checks whether the ordinals of Measures and calculated measures are
* unique.
*
* @param cubeName the name of the cube (required for error-messages only)
* @param measures the stored measures
* @param xmlCalcMembers the calculate members (only members of dimension Measures are checked)
*/
private void checkOrdinals(
String cubeName,
RolapStoredMeasure measures[],
MondrianDef.CalculatedMember[] xmlCalcMembers)
{
Map ordinals = new HashMap();

// step 1: check the stored measures
for (int i = 0; i < measures.length; i++) {
Integer ordinal = new Integer(measures[i].getOrdinal());
if (!ordinals.containsKey(ordinal)) {
ordinals.put(ordinal, measures[i].getUniqueName());
} else {
throw MondrianResource.instance().MeasureOrdinalsNotUnique.ex(
cubeName,
ordinal.toString(),
(String) ordinals.get(ordinal),
measures[i].getUniqueName());
}
}

// step 2: check the calculated measures
for (int i = 0; i < xmlCalcMembers.length; i++) {
if (xmlCalcMembers[i].dimension.equalsIgnoreCase("Measures")) {
MondrianDef.CalculatedMemberProperty[] properties =
xmlCalcMembers[i].memberProperties;

for (int j = 0; j < properties.length; j++) {
if (properties[j].name.equals(
Property.MEMBER_ORDINAL.getName())) {
Integer ordinal = new Integer(properties[j].value);

final String uname =
"[Measures].[" + xmlCalcMembers[i].name + "]";
if (!ordinals.containsKey(ordinal)) {
ordinals.put(ordinal, uname);
} else {
throw MondrianResource.instance().
MeasureOrdinalsNotUnique.ex(
cubeName,
ordinal.toString(),
(String)ordinals.get(ordinal),
uname);
}
}
}
}
}
}

/**
* Adds a collection of calculated members and named sets to this cube.
* The members and sets can refer to each other.
Expand Down Expand Up @@ -546,6 +606,9 @@ private void preCalcMember(
validateMemberProps(xmlProperties, propNames, propExprs,
xmlCalcMember.name);

final int measureCount =
this.measuresHierarchy.memberReader.getMemberCount();

// Generate SQL.
assert memberUniqueName.startsWith("[");
buf.append("MEMBER ").append(memberUniqueName)
Expand All @@ -563,8 +626,16 @@ private void preCalcMember(
}
// Flag that the calc members are defined against a cube; will
// determine the value of Member.isCalculatedInQuery
buf.append(",").append(Util.nl)
.append(Property.MEMBER_SCOPE).append(" = 'CUBE'");
buf.append(",").append(Util.nl).
append(Util.quoteMdxIdentifier(Property.MEMBER_SCOPE.name)).
append(" = 'CUBE'");

// Assign the member an ordinal higher than all of the stored measures.
if (!propNames.contains(Property.MEMBER_ORDINAL)) {
buf.append(",").append(Util.nl).
append(Property.MEMBER_ORDINAL).append(" = ").
append(measureCount + j);
}
buf.append(Util.nl);
}

Expand Down Expand Up @@ -716,29 +787,29 @@ CellReader getCellReader() {
return this.cellReader;
}

/**
/**
* Returns true if this Cube is either virtual or if the Cube's
* RolapStar is caching aggregates.
*
* @return
*
* @return
*/
public boolean isCacheAggregations() {
return (isVirtual()) ? true : star.isCacheAggregations();
}

/**
/**
* Set if this (non-virtual) Cube's RolapStar should cache
* aggregations.
*
* @param cache
*
* @param cache
*/
public void setCacheAggregations(boolean cache) {
if (! isVirtual()) {
star.setCacheAggregations(cache);
}
}

/**
/**
* Clear the in memory aggregate cache associated with this Cube, but
* only if Disabling Caching has been enabled.
*/
Expand Down

0 comments on commit 1a6fc9a

Please sign in to comment.