Skip to content

Commit

Permalink
MONDRIAN: Partial fix for bug MONDRIAN-960, "Ability to define non-me…
Browse files Browse the repository at this point in the history
…asure

    calculated members in a cube under a specifc parent", adding attributes
    'hierarchy' and 'parent' to metamodel element 'CalculatedMember'.

    Implement new name-resolution algorithm (class NameResolver) that solves
    problems such as resolving names of calculated members. Enabled only if
    SsasCompatibleNaming=true (will become sole algorithm in mondrian-4.).

    Fix several test exceptions when SsasCompatibleNaming=true.

[git-p4: depot-paths = "//open/mondrian/": change = 14560]
  • Loading branch information
julianhyde committed Aug 24, 2011
1 parent 24db677 commit 2f5d220
Show file tree
Hide file tree
Showing 45 changed files with 1,357 additions and 415 deletions.
34 changes: 31 additions & 3 deletions src/main/mondrian/olap/DelegatingSchemaReader.java
Expand Up @@ -103,8 +103,8 @@ public final Member getMemberByUniqueName(

public Member getMemberByUniqueName(
List<Id.Segment> uniqueNameParts,
boolean failIfNotFound,
MatchType matchType)
boolean failIfNotFound,
MatchType matchType)
{
return schemaReader.getMemberByUniqueName(
uniqueNameParts, failIfNotFound, matchType);
Expand All @@ -118,7 +118,35 @@ public final OlapElement lookupCompound(
parent, names, failIfNotFound, category, MatchType.EXACT);
}

public OlapElement lookupCompound(
public final OlapElement lookupCompound(
OlapElement parent,
List<Id.Segment> names,
boolean failIfNotFound,
int category,
MatchType matchType)
{
if (MondrianProperties.instance().SsasCompatibleNaming.get()) {
return new NameResolver().resolve(
parent,
Util.toOlap4j(names),
failIfNotFound,
category,
matchType,
getNamespaces());
}
return lookupCompoundInternal(
parent,
names,
failIfNotFound,
category,
matchType);
}

public List<NameResolver.Namespace> getNamespaces() {
return schemaReader.getNamespaces();
}

public OlapElement lookupCompoundInternal(
OlapElement parent, List<Id.Segment> names,
boolean failIfNotFound, int category, MatchType matchType)
{
Expand Down
45 changes: 15 additions & 30 deletions src/main/mondrian/olap/DimensionBase.java
Expand Up @@ -4,17 +4,16 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2001-2002 Kana Software, Inc.
// Copyright (C) 2001-2009 Julian Hyde and others
// Copyright (C) 2001-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 6 August, 2001
*/

package mondrian.olap;

import mondrian.resource.MondrianResource;

import java.util.List;

/**
* Abstract implementation for a {@link Dimension}.
*
Expand Down Expand Up @@ -110,40 +109,26 @@ public OlapElement lookupChild(
OlapElement oeLevel =
getHierarchy().lookupChild(schemaReader, s, matchType);
if (oeLevel != null) {
oe = oeLevel; // level match overrides hierarchy match
return oeLevel; // level match overrides hierarchy match
}
}
return oe;
} else {
// New (SSAS-compatible) behavior. If there is no matching
// hierarchy, find the first level with the given name.
if (oe == null) {
for (Hierarchy hierarchy
: schemaReader.getDimensionHierarchies(this))
{
oe = hierarchy.lookupChild(schemaReader, s, matchType);
if (oe != null) {
break;
}
}
if (oe != null) {
return oe;
}
}

if (getLogger().isDebugEnabled()) {
StringBuilder buf = new StringBuilder(64);
buf.append("DimensionBase.lookupChild: ");
buf.append("name=");
buf.append(getName());
buf.append(", childname=");
buf.append(s);
if (oe == null) {
buf.append(" returning null");
} else {
buf.append(" returning elementname=" + oe.getName());
final List<Hierarchy> hierarchyList =
schemaReader.getDimensionHierarchies(this);
for (Hierarchy hierarchy : hierarchyList) {
oe = hierarchy.lookupChild(schemaReader, s, matchType);
if (oe != null) {
return oe;
}
}
getLogger().debug(buf.toString());
return null;
}

return oe;
}

public boolean isHighCardinality() {
Expand Down
54 changes: 42 additions & 12 deletions src/main/mondrian/olap/Formula.java
Expand Up @@ -7,11 +7,9 @@
// Copyright (C) 2001-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 1 March, 2000
*/

package mondrian.olap;

import mondrian.olap.type.*;
import mondrian.resource.MondrianResource;
import mondrian.mdx.MemberExpr;
Expand Down Expand Up @@ -169,47 +167,79 @@ void accept(Validator validator) {
*/
void createElement(Query q) {
// first resolve the name, bit by bit
final List<Id.Segment> segments = id.getSegments();
if (isMember) {
if (mdxMember != null) {
return;
}
OlapElement mdxElement = q.getCube();
final SchemaReader schemaReader = q.getSchemaReader(false);
for (int i = 0; i < id.getSegments().size(); i++) {
Id.Segment segment = id.getSegments().get(i);
for (int i = 0; i < segments.size(); i++) {
final Id.Segment segment = segments.get(i);
OlapElement parent = mdxElement;
mdxElement = null;
// The last segment of the id is the name of the calculated
// member so no need to look for a pre-existing child. This
// avoids unnecessarily executing SQL and loading children into
// cache.
if (i != id.getSegments().size() - 1) {
if (i != segments.size() - 1) {
mdxElement = schemaReader.getElementChild(parent, segment);
}

// Don't try to look up the member which the formula is
// defining. We would only find one if the member is overriding
// a member at the cube or schema level, and we don't want to
// change that member's properties.
if (mdxElement == null || i == id.getSegments().size() - 1) {
if (mdxElement == null || i == segments.size() - 1) {
// this part of the name was not found... define it
Level level;
Member parentMember = null;
if (parent instanceof Member) {
parentMember = (Member) parent;
level = parentMember.getLevel().getChildLevel();
if (level == null) {
throw Util.newError(
"The '"
+ segment.name
+ "' calculated member cannot be created "
+ "because its parent is at the lowest level "
+ "in the "
+ parentMember.getHierarchy().getUniqueName()
+ " hierarchy.");
}
} else {
Hierarchy hierarchy = parent.getHierarchy();
final Hierarchy hierarchy;
if (parent instanceof Dimension
&& MondrianProperties.instance()
.SsasCompatibleNaming.get())
{
Dimension dimension = (Dimension) parent;
if (dimension.getHierarchies().length == 1) {
hierarchy = dimension.getHierarchies()[0];
} else {
hierarchy = null;
}
} else {
hierarchy = parent.getHierarchy();
}
if (hierarchy == null) {
throw MondrianResource.instance()
.MdxCalculatedHierarchyError.ex(id.toString());
}
level = hierarchy.getLevels()[0];
}
if (parentMember != null
&& parentMember.isCalculated())
{
throw Util.newError(
"The '"
+ parent
+ "' calculated member cannot be used as a parent"
+ " of another calculated member.");
}
Member mdxMember =
level.getHierarchy().createMember(
parentMember, level, id.getSegments().get(i).name,
this);
parentMember, level, segment.name, this);
assert mdxMember != null;
mdxElement = mdxMember;
}
Expand All @@ -218,14 +248,14 @@ void createElement(Query q) {
} else {
// don't need to tell query... it's already in query.formula
Util.assertTrue(
id.getSegments().size() == 1,
segments.size() == 1,
"set names must not be compound");
// Caption and description are initialized to null, and annotations
// to the empty map. If named set is defined in the schema, we will
// give these their true values later.
mdxSet =
new SetBase(
id.getSegments().get(0).name,
segments.get(0).name,
null,
null,
exp,
Expand Down
19 changes: 15 additions & 4 deletions src/main/mondrian/olap/Hierarchy.java
Expand Up @@ -4,19 +4,18 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 1999-2002 Kana Software, Inc.
// Copyright (C) 2001-2009 Julian Hyde and others
// Copyright (C) 2001-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 2 March, 1999
*/

package mondrian.olap;

import java.util.Map;

/**
* A <code>Hierarchy</code> is a set of members, organized into levels.
*
* @version $Id$
*/
public interface Hierarchy extends OlapElement, Annotated {
/**
Expand Down Expand Up @@ -65,6 +64,18 @@ public interface Hierarchy extends OlapElement, Annotated {
*/
Member createMember(
Member parent, Level level, String name, Formula formula);

/**
* Returns the unique name of this hierarchy, always including the dimension
* name, e.g. "[Time].[Time]", regardless of whether
* {@link MondrianProperties#SsasCompatibleNaming} is enabled.
*
* @deprecated Will be removed in mondrian-4.0, when
* {@link #getUniqueName()} will have this behavior.
*
* @return Unique name of hierarchy.
*/
String getUniqueNameSsas();
}

// End Hierarchy.java
9 changes: 5 additions & 4 deletions src/main/mondrian/olap/HierarchyBase.java
Expand Up @@ -4,13 +4,10 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2001-2002 Kana Software, Inc.
// Copyright (C) 2001-2009 Julian Hyde and others
// Copyright (C) 2001-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 6 August, 2001
*/

package mondrian.olap;

import mondrian.resource.MondrianResource;
Expand Down Expand Up @@ -118,6 +115,10 @@ public String getUniqueName() {
return uniqueName;
}

public String getUniqueNameSsas() {
return Util.makeFqName(dimension, name);
}

public String getName() {
return name;
}
Expand Down
8 changes: 6 additions & 2 deletions src/main/mondrian/olap/MatchType.java
Expand Up @@ -3,7 +3,7 @@
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2003-2009 Julian Hyde
// Copyright (C) 2003-2011 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
Expand All @@ -26,7 +26,11 @@ public enum MatchType {
/** If no exact match, return the preceding member */
BEFORE,
/** If no exact match, return the next member */
AFTER;
AFTER,
/** Return the first child */
FIRST,
/** Return the last child */
LAST;

/**
* Return true if either Exact or Exact Schema value
Expand Down
17 changes: 15 additions & 2 deletions src/main/mondrian/olap/Mondrian.xml
Expand Up @@ -1066,9 +1066,22 @@ Revision is $Id$
Equivalent to the Formula sub-element.
</Doc>
</Attribute>
<Attribute name="dimension" required="true">
<Attribute name="dimension" required="false">
<Doc>
<p>Name of the dimension that this member belongs to.</p>
<p>Deprecated: use {@code hierarchy} attribute instead.</p>
</Doc>
</Attribute>
<Attribute name="hierarchy" required="false">
<Doc>
<p>Name of the hierarchy that this member belongs to.</p>
</Doc>
</Attribute>
<Attribute name="parent" required="false">
<Doc>
Name of the dimension which this member belongs to.
Fully-qualified name of the parent member.
If not specified, the member will be at the lowest level
(besides the 'all' level) in the hierarchy.
</Doc>
</Attribute>
<Attribute name="visible" type="Boolean" required="false">
Expand Down

0 comments on commit 2f5d220

Please sign in to comment.