diff --git a/src/org/olap4j/driver/xmla/MetadataReader.java b/src/org/olap4j/driver/xmla/MetadataReader.java
new file mode 100644
index 0000000..b53fee6
--- /dev/null
+++ b/src/org/olap4j/driver/xmla/MetadataReader.java
@@ -0,0 +1,88 @@
+/*
+// This software is subject to the terms of the Common Public License
+// Agreement, available at the following URL:
+// http://www.opensource.org/licenses/cpl.html.
+// Copyright (C) 2008-2008 Julian Hyde
+// All Rights Reserved.
+// You must accept the terms of that agreement to use this software.
+*/
+package org.olap4j.driver.xmla;
+
+import org.olap4j.OlapException;
+import org.olap4j.metadata.Member;
+
+import java.util.*;
+
+/**
+ * Can read metadata, in particular members.
+ *
+ * @author jhyde
+ * @version $Id: $
+ * @since Jan 14, 2008
+ */
+interface MetadataReader {
+ /**
+ * Looks up a member by its unique name.
+ *
+ *
Not part of public olap4j API.
+ *
+ * @param memberUniqueName Unique name of member
+ * @return Member, or null if not found
+ * @throws org.olap4j.OlapException if error occurs
+ */
+ XmlaOlap4jMember lookupMemberByUniqueName(
+ String memberUniqueName)
+ throws OlapException;
+
+ /**
+ * Looks up a list of members by their unique name and writes the results
+ * into a map.
+ *
+ *
Not part of public olap4j API.
+ *
+ * @param memberUniqueNames List of unique names of member
+ *
+ * @param memberMap Map to populate with members
+ *
+ * @throws org.olap4j.OlapException if error occurs
+ */
+ void lookupMembersByUniqueName(
+ List memberUniqueNames,
+ Map memberMap)
+ throws OlapException;
+
+ /**
+ * Looks a member by its unique name and returns members related by
+ * the specified tree-operations.
+ *
+ * Not part of public olap4j API.
+ *
+ * @param memberUniqueName Unique name of member
+ *
+ * @param treeOps Collection of tree operations to travel relative to
+ * given member in order to create list of members
+ *
+ * @param list list to be populated with members related to the given
+ * member, or empty set if the member is not found
+ *
+ * @throws org.olap4j.OlapException if error occurs
+ */
+ void lookupMemberRelatives(
+ Set treeOps,
+ String memberUniqueName,
+ List list) throws OlapException;
+
+ /**
+ * Looks up members of a given level.
+ *
+ * @param level Level
+ *
+ * @throws org.olap4j.OlapException if error occurs
+ *
+ * @return list of members at in the level
+ */
+ List getLevelMembers(XmlaOlap4jLevel level)
+ throws OlapException;
+}
+
+// End MetadataReader.java
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
index 9732690..4cae9b4 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
@@ -182,6 +182,32 @@ void populate() throws OlapException {
int ordinal = 0;
final Map propertyValues =
new HashMap();
+
+ // First pass, gather up a list of member unique names to fetch
+ // all at once.
+ //
+ // NOTE: This approach allows the driver to fetch a large number
+ // of members in one round trip, which is much more efficient.
+ // However, if the axis has a very large number of members, the map
+ // may use too much memory. This is an unresolved issue.
+ final MetadataReader metadataReader =
+ metaData.cube.getMetadataReader();
+ final Map memberMap =
+ new HashMap();
+ List uniqueNames = new ArrayList();
+ for (Element tupleNode
+ : findChildren(tuplesNode, MDDATASET_NS, "Tuple"))
+ {
+ for (Element memberNode
+ : findChildren(tupleNode, MDDATASET_NS, "Member"))
+ {
+ final String uname = stringElement(memberNode, "UName");
+ uniqueNames.add(uname);
+ }
+ }
+ metadataReader.lookupMembersByUniqueName(uniqueNames, memberMap);
+
+ // Second pass, populate the axis.
for (Element tupleNode
: findChildren(tuplesNode, MDDATASET_NS, "Tuple"))
{
@@ -192,8 +218,7 @@ void populate() throws OlapException {
String hierarchyName =
memberNode.getAttribute("Hierarchy");
final String uname = stringElement(memberNode, "UName");
- Member member =
- metaData.cube.lookupMemberByUniqueName(uname);
+ Member member = memberMap.get(uname);
if (member == null) {
final String caption =
stringElement(memberNode, "Caption");
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
index 8d0d673..752a409 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
@@ -396,7 +396,7 @@ void populateList(
Context context,
MetadataRequest metadataRequest,
Handler handler,
- String... restrictions) throws OlapException
+ Object[] restrictions) throws OlapException
{
String request =
generateRequest(context, metadataRequest, restrictions);
@@ -474,10 +474,24 @@ Element xxx(String request) throws OlapException {
return findChild(returnElement, ROWSET_NS, "root");
}
+ /**
+ * Generates a metadata request.
+ *
+ * The list of restrictions must have even length. Even elements must
+ * be a string (the name of the restriction); odd elements must be either
+ * a string (the value of the restriction) or a list of strings (multiple
+ * values of the restriction)
+ *
+ *
+ * @param context Context
+ * @param metadataRequest Metadata request
+ * @param restrictions List of restrictions
+ * @return XMLA request
+ */
public String generateRequest(
Context context,
MetadataRequest metadataRequest,
- String... restrictions)
+ Object[] restrictions)
{
final String dataSourceInfo =
context.olap4jConnection.getDataSourceInfo();
@@ -499,11 +513,23 @@ public String generateRequest(
throw new IllegalArgumentException();
}
for (int i = 0; i < restrictions.length; i += 2) {
- final String restriction = restrictions[i];
- final String value = restrictions[i + 1];
- buf.append("<").append(restriction).append(">");
- buf.append(xmlEncode(value));
- buf.append("").append(restriction).append(">");
+ final String restriction = (String) restrictions[i];
+ final Object o = restrictions[i + 1];
+ if (o instanceof String) {
+ buf.append("<").append(restriction).append(">");
+ final String value = (String) o;
+ buf.append(xmlEncode(value));
+ buf.append("").append(restriction).append(">");
+
+ } else {
+ //noinspection unchecked
+ List valueList = (List) o;
+ for (String value : valueList) {
+ buf.append("<").append(restriction).append(">");
+ buf.append(xmlEncode(value));
+ buf.append("").append(restriction).append(">");
+ }
+ }
}
}
buf.append(" \n"
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java
index 515f224..57f7afd 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java
@@ -14,6 +14,7 @@
import org.olap4j.metadata.*;
import java.util.*;
+import java.lang.ref.SoftReference;
/**
* Implementation of {@link Cube}
@@ -43,6 +44,7 @@ class XmlaOlap4jCube implements Cube, Named
new NamedListImpl();
private final NamedList namedSets =
new NamedListImpl();
+ private final MetadataReader metadataReader;
XmlaOlap4jCube(
XmlaOlap4jSchema olap4jSchema,
@@ -55,6 +57,11 @@ class XmlaOlap4jCube implements Cube, Named
this.olap4jSchema = olap4jSchema;
this.name = name;
this.description = description;
+ this.metadataReader =
+ new CachingMetadataReader(
+ new RawMetadataReader()
+ )
+ ;
final XmlaOlap4jConnection olap4jConnection =
olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
@@ -168,117 +175,26 @@ public Member lookupMember(String... nameParts) throws OlapException {
private Member lookupMember(
List segmentList) throws OlapException
{
- if (true) {
- StringBuilder buf = new StringBuilder();
- for (IdentifierNode.Segment segment : segmentList) {
- if (buf.length() > 0) {
- buf.append('.');
- }
- buf.append(segment.toString());
- }
- final String uniqueName = buf.toString();
- return lookupMemberByUniqueName(uniqueName);
- } else {
- final Hierarchy hierarchy =
- getHierarchies().get(segmentList.get(0).getName());
- final NamedList rootMembers = hierarchy.getRootMembers();
- Member member = rootMembers.get(segmentList.get(1).getName());
- int k = 1;
- if (member == null) {
- if (rootMembers.size() == 1
- && rootMembers.get(0).isAll()) {
- member = rootMembers.get(0);
- ++k;
- } else {
- return null;
- }
- }
- while (k < segmentList.size()) {
-
+ StringBuilder buf = new StringBuilder();
+ for (IdentifierNode.Segment segment : segmentList) {
+ if (buf.length() > 0) {
+ buf.append('.');
}
- return member;
- }
- }
-
- /**
- * Looks up a member by its unique name.
- *
- * Not part of public olap4j API.
- *
- * @param memberUniqueName Unique name of member
- * @return Member, or null if not found
- * @throws OlapException if error occurs
- */
- XmlaOlap4jMember lookupMemberByUniqueName(
- String memberUniqueName)
- throws OlapException
- {
- NamedList list =
- new NamedListImpl();
- lookupMembersByUniqueName(
- EnumSet.of(Member.TreeOp.SELF), memberUniqueName, list);
- switch (list.size()) {
- case 0:
- return null;
- case 1:
- return list.get(0);
- default:
- throw new IllegalArgumentException(
- "more than one member with unique name '"
- + memberUniqueName
- + "'");
+ buf.append(segment.toString());
}
+ final String uniqueName = buf.toString();
+ return getMetadataReader().lookupMemberByUniqueName(uniqueName);
}
/**
- * Looks a member by its unique name and returns members related by
- * the specified tree-operations.
+ * Returns this cube's metadata reader.
*
* Not part of public olap4j API.
*
- * @param memberUniqueName Unique name of member
- *
- * @param treeOps Collection of tree operations to travel relative to
- * given member in order to create list of members
- *
- * @param list list to be populated with members related to the given
- * member, or empty set if the member is not found
- *
- * @throws OlapException if error occurs
- */
- void lookupMembersByUniqueName(
- Set treeOps,
- String memberUniqueName,
- List list) throws OlapException
- {
- final XmlaOlap4jConnection.Context context =
- new XmlaOlap4jConnection.Context(this, null, null, null);
- int treeOpMask = 0;
- for (Member.TreeOp treeOp : treeOps) {
- treeOpMask |= treeOp.xmlaOrdinal();
- }
- olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection
- .populateList(
- list,
- context,
- XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_MEMBERS,
- new XmlaOlap4jConnection.MemberHandler(),
- "CATALOG_NAME", olap4jSchema.olap4jCatalog.getName(),
- "SCHEMA_NAME", olap4jSchema.getName(),
- "CUBE_NAME", getName(),
- "MEMBER_UNIQUE_NAME", memberUniqueName,
- "TREE_OP", String.valueOf(treeOpMask));
- }
-
- /**
- * Returns true if two objects are equal, or are both null.
- *
- * @param s First object
- * @param t Second object
- * @return whether objects are equal
+ * @return metadata reader
*/
- static boolean equal(T s, T t) {
- return (s == null) ? (t == null) : s.equals(t);
+ MetadataReader getMetadataReader() {
+ return metadataReader;
}
public List lookupMembers(
@@ -295,80 +211,248 @@ public List lookupMembers(
final String uniqueName = buf.toString();
final List list =
new ArrayList();
- lookupMembersByUniqueName(treeOps, uniqueName, list);
-// Collections.sort(list, new MemberComparator());
+ getMetadataReader().lookupMemberRelatives(
+ treeOps, uniqueName, list);
return Olap4jUtil.cast(list);
}
/**
- * Looks a member by its unique name and returns members related by
- * the specified tree-operations.
- *
- * Not part of public olap4j API.
- *
- * @param level Level
- *
- * @param list list to be populated with members related to the given level
- *
- * @throws OlapException if error occurs
+ * Abstract implementation of MemberReader that delegates all operations
+ * to an underlying MemberReader.
*/
- void lookupLevelMembers(
- XmlaOlap4jLevel level,
- List list) throws OlapException
+ private static abstract class DelegatingMetadataReader
+ implements MetadataReader
{
- assert level.olap4jHierarchy.olap4jDimension.olap4jCube == this;
- final XmlaOlap4jConnection.Context context =
- new XmlaOlap4jConnection.Context(level);
- olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection
- .populateList(
- list,
- context,
- XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_MEMBERS,
- new XmlaOlap4jConnection.MemberHandler(),
- "CATALOG_NAME", olap4jSchema.olap4jCatalog.getName(),
- "SCHEMA_NAME", olap4jSchema.getName(),
- "CUBE_NAME", getName(),
- "DIMENSION_UNIQUE_NAME",
- level.olap4jHierarchy.olap4jDimension.getUniqueName(),
- "HIERARCHY_UNIQUE_NAME",
- level.olap4jHierarchy.getUniqueName(),
- "LEVEL_UNIQUE_NAME", level.getUniqueName());
+ private final MetadataReader metadataReader;
+
+ DelegatingMetadataReader(MetadataReader metadataReader) {
+ this.metadataReader = metadataReader;
+ }
+
+ public XmlaOlap4jMember lookupMemberByUniqueName(
+ String memberUniqueName) throws OlapException
+ {
+ return metadataReader.lookupMemberByUniqueName(memberUniqueName);
+ }
+
+ public void lookupMembersByUniqueName(
+ List memberUniqueNames,
+ Map memberMap) throws OlapException
+ {
+ metadataReader.lookupMembersByUniqueName(
+ memberUniqueNames, memberMap);
+ }
+
+ public void lookupMemberRelatives(
+ Set treeOps,
+ String memberUniqueName,
+ List list) throws OlapException
+ {
+ metadataReader.lookupMemberRelatives(
+ treeOps, memberUniqueName, list);
+ }
+
+ public List getLevelMembers(
+ XmlaOlap4jLevel level)
+ throws OlapException
+ {
+ return metadataReader.getLevelMembers(level);
+ }
}
- // NOT USED
- private static class MemberComparator
- implements Comparator
+ /**
+ * Implementation of MemberReader that reads from an underlying member
+ * reader and caches the results.
+ *
+ * Caches are {@link Map}s containing
+ * {@link java.lang.ref.SoftReference}s to cached objects, so can be
+ * cleared when memory is in short supply.
+ */
+ private static class CachingMetadataReader
+ extends DelegatingMetadataReader
{
- public int compare(XmlaOlap4jMember m1, XmlaOlap4jMember m2) {
- if (equal(m1, m2)) {
- return 0;
+ private final Map> memberMap =
+ new HashMap>();
+
+ private final Map>>
+ levelMemberListMap =
+ new HashMap>>();
+
+ CachingMetadataReader(MetadataReader metadataReader) {
+ super(metadataReader);
+ }
+
+ public XmlaOlap4jMember lookupMemberByUniqueName(
+ String memberUniqueName) throws OlapException
+ {
+ final SoftReference memberRef =
+ memberMap.get(memberUniqueName);
+ if (memberRef != null) {
+ final XmlaOlap4jMember member = memberRef.get();
+ if (member != null) {
+ return member;
+ }
}
- while (true) {
- int depth1 = m1.getDepth(),
- depth2 = m2.getDepth();
- if (depth1 < depth2) {
- m2 = m2.getParentMember();
- if (Olap4jUtil.equal(m1, m2)) {
- return -1;
- }
- } else if (depth1 > depth2) {
- m1 = m1.getParentMember();
- if (equal(m1, m2)) {
- return 1;
- }
+ final XmlaOlap4jMember member =
+ super.lookupMemberByUniqueName(memberUniqueName);
+ memberMap.put(
+ memberUniqueName,
+ new SoftReference(member));
+ return member;
+ }
+
+ public void lookupMembersByUniqueName(
+ List memberUniqueNames,
+ Map memberMap) throws OlapException
+ {
+ final ArrayList remainingMemberUniqueNames =
+ new ArrayList();
+ for (String memberUniqueName : memberUniqueNames) {
+ final SoftReference memberRef =
+ this.memberMap.get(memberUniqueName);
+ final XmlaOlap4jMember member;
+ if (memberRef != null &&
+ (member = memberRef.get()) != null) {
+ memberMap.put(memberUniqueName, member);
} else {
- m1 = m1.getParentMember();
- m2 = m2.getParentMember();
- if (equal(m1, m2)) {
- // The previous values of m1 and m2 are siblings.
- // We do not have access to the ordering key, if
- // we assume that (a) the siblings were returned in
- // the correct order, and (b) the sort is stable,
- // then the first member is the earlier one.
- return -1;
- }
+ remainingMemberUniqueNames.add(memberUniqueName);
}
}
+ // If any of the member names were not in the cache, look them up
+ // by delegating.
+ if (!remainingMemberUniqueNames.isEmpty()) {
+ super.lookupMembersByUniqueName(
+ memberUniqueNames,
+ memberMap);
+ }
+ }
+
+ public List getLevelMembers(
+ XmlaOlap4jLevel level)
+ throws OlapException
+ {
+ final SoftReference> memberListRef =
+ levelMemberListMap.get(level);
+ if (memberListRef != null) {
+ final List memberList = memberListRef.get();
+ if (memberList != null) {
+ return memberList;
+ }
+ }
+ final List memberList =
+ super.getLevelMembers(level);
+ levelMemberListMap.put(
+ level,
+ new SoftReference>(memberList));
+ return memberList;
+ }
+ }
+
+ /**
+ * Implementation of MetadataReader that reads from the XMLA provider,
+ * without caching.
+ */
+ private class RawMetadataReader implements MetadataReader {
+ public XmlaOlap4jMember lookupMemberByUniqueName(
+ String memberUniqueName)
+ throws OlapException
+ {
+ NamedList list =
+ new NamedListImpl();
+ lookupMemberRelatives(
+ EnumSet.of(Member.TreeOp.SELF), memberUniqueName, list);
+ switch (list.size()) {
+ case 0:
+ return null;
+ case 1:
+ return list.get(0);
+ default:
+ throw new IllegalArgumentException(
+ "more than one member with unique name '"
+ + memberUniqueName
+ + "'");
+ }
+ }
+
+ public void lookupMembersByUniqueName(
+ List memberUniqueNames,
+ Map memberMap) throws OlapException
+ {
+ final XmlaOlap4jConnection.Context context =
+ new XmlaOlap4jConnection.Context(
+ XmlaOlap4jCube.this, null, null, null);
+ List memberList =
+ new ArrayList();
+ olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection
+ .populateList(
+ memberList,
+ context,
+ XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_MEMBERS,
+ new XmlaOlap4jConnection.MemberHandler(),
+ new Object[] {
+ "CATALOG_NAME", olap4jSchema.olap4jCatalog.getName(),
+ "SCHEMA_NAME", olap4jSchema.getName(),
+ "CUBE_NAME", getName(),
+ "MEMBER_UNIQUE_NAME", memberUniqueNames
+ });
+ for (XmlaOlap4jMember member : memberList) {
+ memberMap.put(member.getUniqueName(), member);
+ }
+ }
+
+ public void lookupMemberRelatives(
+ Set treeOps,
+ String memberUniqueName,
+ List list) throws OlapException
+ {
+ final XmlaOlap4jConnection.Context context =
+ new XmlaOlap4jConnection.Context(
+ XmlaOlap4jCube.this, null, null, null);
+ int treeOpMask = 0;
+ for (Member.TreeOp treeOp : treeOps) {
+ treeOpMask |= treeOp.xmlaOrdinal();
+ }
+ olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection
+ .populateList(
+ list,
+ context,
+ XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_MEMBERS,
+ new XmlaOlap4jConnection.MemberHandler(),
+ new Object[] {
+ "CATALOG_NAME", olap4jSchema.olap4jCatalog.getName(),
+ "SCHEMA_NAME", olap4jSchema.getName(),
+ "CUBE_NAME", getName(),
+ "MEMBER_UNIQUE_NAME", memberUniqueName,
+ "TREE_OP", String.valueOf(treeOpMask)
+ });
+ }
+
+ public List getLevelMembers(
+ XmlaOlap4jLevel level)
+ throws OlapException
+ {
+ assert level.olap4jHierarchy.olap4jDimension.olap4jCube
+ == XmlaOlap4jCube.this;
+ final XmlaOlap4jConnection.Context context =
+ new XmlaOlap4jConnection.Context(level);
+ List list = new ArrayList();
+ olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection
+ .populateList(
+ list,
+ context,
+ XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_MEMBERS,
+ new XmlaOlap4jConnection.MemberHandler(),
+ new Object[] {
+ "CATALOG_NAME", olap4jSchema.olap4jCatalog.getName(),
+ "SCHEMA_NAME", olap4jSchema.getName(),
+ "CUBE_NAME", getName(),
+ "DIMENSION_UNIQUE_NAME",
+ level.olap4jHierarchy.olap4jDimension.getUniqueName(),
+ "HIERARCHY_UNIQUE_NAME",
+ level.olap4jHierarchy.getUniqueName(),
+ "LEVEL_UNIQUE_NAME", level.getUniqueName()
+ });
+ return list;
}
}
}
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jHierarchy.java b/src/org/olap4j/driver/xmla/XmlaOlap4jHierarchy.java
index 244b24b..1a05c6d 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jHierarchy.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jHierarchy.java
@@ -12,6 +12,8 @@
import org.olap4j.impl.*;
import org.olap4j.metadata.*;
+import java.util.List;
+
/**
* Implementation of {@link org.olap4j.metadata.Hierarchy}
* for XML/A providers.
@@ -37,7 +39,7 @@ class XmlaOlap4jHierarchy
String caption,
String description,
boolean all,
- String defaultMemberUniqueName)
+ String defaultMemberUniqueName) throws OlapException
{
super(uniqueName, name, caption, description);
assert olap4jDimension != null;
@@ -58,26 +60,22 @@ public boolean hasAll() {
return all;
}
- public Member getDefaultMember() {
- try {
- return olap4jDimension.olap4jCube.lookupMemberByUniqueName(
- defaultMemberUniqueName);
- } catch (OlapException e) {
- // TODO: cache member in hierarchy on creation, and obsolete the
- // defaultMemberUniqueName field; do not make this method throw
- // OlapException
- throw new RuntimeException(
- "Internal error: lookup up default member" +
- defaultMemberUniqueName,
- e);
+ public Member getDefaultMember() throws OlapException {
+ if (defaultMemberUniqueName == null) {
+ return null;
}
+ return olap4jDimension.olap4jCube.getMetadataReader()
+ .lookupMemberByUniqueName(
+ defaultMemberUniqueName);
}
public NamedList getRootMembers() throws OlapException {
+ final List memberList =
+ olap4jDimension.olap4jCube.getMetadataReader().getLevelMembers(
+ levels.get(0));
final NamedList list =
new NamedListImpl();
- olap4jDimension.olap4jCube.lookupLevelMembers(
- levels.get(0), list);
+ list.addAll(memberList);
return Olap4jUtil.cast(list);
}
}
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java b/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java
index 2b5c4b8..421fbb8 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java
@@ -57,20 +57,22 @@ protected void populateList(
{
context.olap4jConnection.populateList(
list, context, metadataRequest, handler,
- "CATALOG_NAME",
- olap4jHierarchy.olap4jDimension.olap4jCube.olap4jSchema
- .olap4jCatalog.getName(),
- "SCHEMA_NAME",
- olap4jHierarchy.olap4jDimension.olap4jCube.olap4jSchema
- .getName(),
- "CUBE_NAME",
- olap4jHierarchy.olap4jDimension.olap4jCube.getName(),
- "DIMENSION_UNIQUE_NAME",
- olap4jHierarchy.olap4jDimension.getUniqueName(),
- "HIERARCHY_UNIQUE_NAME",
- olap4jHierarchy.getUniqueName(),
- "LEVEL_UNIQUE_NAME",
- getUniqueName());
+ new Object[] {
+ "CATALOG_NAME",
+ olap4jHierarchy.olap4jDimension.olap4jCube.olap4jSchema
+ .olap4jCatalog.getName(),
+ "SCHEMA_NAME",
+ olap4jHierarchy.olap4jDimension.olap4jCube.olap4jSchema
+ .getName(),
+ "CUBE_NAME",
+ olap4jHierarchy.olap4jDimension.olap4jCube.getName(),
+ "DIMENSION_UNIQUE_NAME",
+ olap4jHierarchy.olap4jDimension.getUniqueName(),
+ "HIERARCHY_UNIQUE_NAME",
+ olap4jHierarchy.getUniqueName(),
+ "LEVEL_UNIQUE_NAME",
+ getUniqueName()
+ });
}
};
}
@@ -106,11 +108,9 @@ protected String getName(Property property) {
}
public List getMembers() throws OlapException {
- final List list =
- new ArrayList();
- olap4jHierarchy.olap4jDimension.olap4jCube.lookupLevelMembers(
- this, list);
- return Olap4jUtil.cast(list);
+ return Olap4jUtil.cast(
+ olap4jHierarchy.olap4jDimension.olap4jCube.getMetadataReader()
+ .getLevelMembers(this));
}
public int getCardinality() {
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java b/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java
index e5b2664..39d7a28 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java
@@ -22,7 +22,7 @@
* TODO:
* - create members with a pointer to their parent member (not the name)
* - implement a member cache (by unique name, belongs to cube, soft)
- * - implement Hierarchy.getRootMembers and Hierarchy.getDefaultMember
+ * - implement Hierarchy.getRootMembers
*
*
* @author jhyde
@@ -82,7 +82,8 @@ public NamedList extends Member> getChildMembers() throws OlapException {
final NamedList list =
new NamedListImpl();
getCube()
- .lookupMembersByUniqueName(
+ .getMetadataReader()
+ .lookupMemberRelatives(
EnumSet.of(TreeOp.CHILDREN),
uniqueName,
list);
@@ -100,7 +101,7 @@ public XmlaOlap4jMember getParentMember() {
if (parentMember == null) {
try {
parentMember =
- getCube()
+ getCube().getMetadataReader()
.lookupMemberByUniqueName(parentMemberUniqueName);
} catch (OlapException e) {
throw new RuntimeException("yuck!"); // FIXME
diff --git a/src/org/olap4j/metadata/Hierarchy.java b/src/org/olap4j/metadata/Hierarchy.java
index bd1734b..e64ff53 100644
--- a/src/org/olap4j/metadata/Hierarchy.java
+++ b/src/org/olap4j/metadata/Hierarchy.java
@@ -61,7 +61,7 @@ public interface Hierarchy extends MetadataElement {
*
* @return the default member of this hierarchy
*/
- Member getDefaultMember();
+ Member getDefaultMember() throws OlapException;
/**
* Returns the root member or members of this Dimension.
@@ -72,13 +72,15 @@ public interface Hierarchy extends MetadataElement {
* The caller should assume that the list is immutable;
* if the caller modifies the list, behavior is undefined.
*
- * The result is similar to calling
- * getLevels().get(0).getMembers()
. The contents will be the
- * same, but this method returns a link {@link NamedList} rather than a
+ *
The result is similar to that returned by
+ * getLevels().get(0).getMembers()
; the contents will be the
+ * same, but this method returns a {@link NamedList} rather than a
* mere {@link java.util.List} because the members of the root level are
* known to have unique names.
*
* @return root members of this hierarchy
+ *
+ * @throws OlapException on database error
*/
NamedList getRootMembers() throws OlapException;
}
diff --git a/src/org/olap4j/query/Query.java b/src/org/olap4j/query/Query.java
index 3b13b2d..3651000 100644
--- a/src/org/olap4j/query/Query.java
+++ b/src/org/olap4j/query/Query.java
@@ -104,7 +104,7 @@ public QueryAxis getunusedAxis() {
return unused;
}
- public boolean validate() {
+ public boolean validate() throws OlapException {
for (Dimension dimension : cube.getDimensions()) {
QueryDimension queryDimension =
getDimension(dimension.getName());
diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java
index 89d7c49..ea49425 100644
--- a/testsrc/org/olap4j/ConnectionTest.java
+++ b/testsrc/org/olap4j/ConnectionTest.java
@@ -1667,15 +1667,20 @@ public void testCellSetBug() throws SQLException {
OlapConnection olapConnection =
((OlapWrapper) connection).unwrap(OlapConnection.class);
final OlapStatement olapStatement = olapConnection.createStatement();
+ // Note: substitute [Sales Ragged] for [Sales] below and the query
+ // takes a very long time against mondrian's XMLA driver, because
+ // mondrian has a performance bug assigning ordinals to ragged
+ // hierarchies, and XMLA requests ask for member ordinals along with
+ // the other attributes of members.
CellSet cellSet =
olapStatement.executeOlapQuery(
"SELECT " +
"{[Product].[All Products].[Drink].[Alcoholic Beverages].Children, [Product].[All Products].[Food].[Baked Goods].Children} ON COLUMNS, " +
"CrossJoin([Store].[All Stores].[USA].[CA].Children, [Time].[1997].[Q1].Children) ON ROWS " +
- "FROM [Sales Ragged]");
+ "FROM [Sales]");
TestContext.assertEqualsVerbose(
TestContext.fold("Axis #0:\n" +
- "{[Measures].[Unit Sales], [Geography].[All Geographys], [Store Size in SQFT].[All Store Size in SQFTs], [Store Type].[All Store Types], [Promotion Media].[All Media], [Promotions].[All Promotions], [Customers].[All Customers], [Education Level].[All Education Levels], [Gender].[All Gender], [Marital Status].[All Marital Status], [Yearly Income].[All Yearly Incomes]}\n" +
+ "{[Measures].[Unit Sales], [Store Size in SQFT].[All Store Size in SQFTs], [Store Type].[All Store Types], [Promotion Media].[All Media], [Promotions].[All Promotions], [Customers].[All Customers], [Education Level].[All Education Levels], [Gender].[All Gender], [Marital Status].[All Marital Status], [Yearly Income].[All Yearly Incomes]}\n" +
"Axis #1:\n" +
"{[Product].[All Products].[Drink].[Alcoholic Beverages].[Beer and Wine]}\n" +
"{[Product].[All Products].[Food].[Baked Goods].[Bread]}\n" +
@@ -1689,6 +1694,9 @@ public void testCellSetBug() throws SQLException {
"{[Store].[All Stores].[USA].[CA].[Los Angeles], [Time].[1997].[Q1].[1]}\n" +
"{[Store].[All Stores].[USA].[CA].[Los Angeles], [Time].[1997].[Q1].[2]}\n" +
"{[Store].[All Stores].[USA].[CA].[Los Angeles], [Time].[1997].[Q1].[3]}\n" +
+ "{[Store].[All Stores].[USA].[CA].[San Diego], [Time].[1997].[Q1].[1]}\n" +
+ "{[Store].[All Stores].[USA].[CA].[San Diego], [Time].[1997].[Q1].[2]}\n" +
+ "{[Store].[All Stores].[USA].[CA].[San Diego], [Time].[1997].[Q1].[3]}\n" +
"{[Store].[All Stores].[USA].[CA].[San Francisco], [Time].[1997].[Q1].[1]}\n" +
"{[Store].[All Stores].[USA].[CA].[San Francisco], [Time].[1997].[Q1].[2]}\n" +
"{[Store].[All Stores].[USA].[CA].[San Francisco], [Time].[1997].[Q1].[3]}\n" +
@@ -1710,12 +1718,18 @@ public void testCellSetBug() throws SQLException {
"Row #7: 51\n" +
"Row #8: 27\n" +
"Row #8: 54\n" +
- "Row #9: 6\n" +
- "Row #9: 2\n" +
- "Row #10: 3\n" +
- "Row #10: 7\n" +
- "Row #11: 2\n" +
- "Row #11: 10\n"),
+ "Row #9: 54\n" +
+ "Row #9: 51\n" +
+ "Row #10: 38\n" +
+ "Row #10: 48\n" +
+ "Row #11: 64\n" +
+ "Row #11: 55\n" +
+ "Row #12: 6\n" +
+ "Row #12: 2\n" +
+ "Row #13: 3\n" +
+ "Row #13: 7\n" +
+ "Row #14: 2\n" +
+ "Row #14: 10\n"),
TestContext.toString(cellSet));
}
diff --git a/testsrc/org/olap4j/OlapTest.java b/testsrc/org/olap4j/OlapTest.java
index 15174f6..911e976 100644
--- a/testsrc/org/olap4j/OlapTest.java
+++ b/testsrc/org/olap4j/OlapTest.java
@@ -10,6 +10,7 @@
import org.olap4j.metadata.*;
import org.olap4j.query.*;
+import org.olap4j.test.TestContext;
import org.w3c.dom.*;
@@ -35,15 +36,17 @@
* @version $Id$
*/
public class OlapTest extends TestCase {
+ final TestContext.Tester tester = TestContext.instance().getTester();
public OlapTest() {
super();
}
public void testModel() {
-
try {
+ Connection jdbcConnection;
+ if (false) {
// define the connection information
String schemaUri = "file:/open/mondrian/demo/FoodMart.xml";
String schemaName = "FoodMart";
@@ -54,11 +57,14 @@ public void testModel() {
// Create a connection object to the specific implementation of an olap4j source
// This is the only provider-specific code
Class.forName("mondrian.olap4j.MondrianOlap4jDriver");
- Connection jdbcConnection = DriverManager.getConnection(
+ jdbcConnection = DriverManager.getConnection(
"jdbc:mondrian:Jdbc=" + jdbc
+ ";User=" + userName
+ ";Password=" + password
+ ";Catalog=" + schemaUri);
+ } else {
+ jdbcConnection = tester.createConnection();
+ }
OlapConnection connection =
((OlapWrapper) jdbcConnection).unwrap(OlapConnection.class);
@@ -69,7 +75,18 @@ public void testModel() {
// The code from here on is generic olap4j stuff
// Get a list of the schemas available from this connection and dump their names
- NamedList schemas = connection.getCatalogs().get("LOCALDB").getSchemas();
+ final String catalogName;
+ switch (tester.getFlavor()) {
+ case MONDRIAN:
+ catalogName = "LOCALDB";
+ break;
+ case XMLA:
+ default:
+ catalogName = "FoodMart";
+ break;
+ }
+ Catalog catalog = connection.getCatalogs().get(catalogName);
+ NamedList schemas = catalog.getSchemas();
for (Schema schema : schemas) {
System.out.println("schema name="+schema.getName());
}
@@ -502,7 +519,9 @@ public static void dimensionSelectionsToXml(
}
}
- public static Element hierarchyToXml(Hierarchy hierarchy, Document doc) {
+ public static Element hierarchyToXml(Hierarchy hierarchy, Document doc)
+ throws OlapException
+ {
Element hierarchyNode;
Element levelNode;
Element nameNode;
diff --git a/testsrc/org/olap4j/XmlaTester.java b/testsrc/org/olap4j/XmlaTester.java
index 7bfdf4f..21dd02a 100644
--- a/testsrc/org/olap4j/XmlaTester.java
+++ b/testsrc/org/olap4j/XmlaTester.java
@@ -25,6 +25,7 @@
*/
public class XmlaTester implements TestContext.Tester {
final XmlaOlap4jDriver.Proxy proxy;
+ private Connection connection;
public XmlaTester()
throws ClassNotFoundException, IllegalAccessException,
@@ -50,6 +51,9 @@ public XmlaTester()
}
public Connection createConnection() throws SQLException {
+ if (connection != null) {
+ return connection;
+ }
try {
Class.forName(DRIVER_CLASS_NAME);
} catch (ClassNotFoundException e) {
@@ -62,10 +66,11 @@ public Connection createConnection() throws SQLException {
XmlaOlap4jDriver.Property.UseThreadProxy.name(), "true");
info.setProperty(
XmlaOlap4jDriver.Property.Catalog.name(), "FoodMart");
- return
+ connection =
DriverManager.getConnection(
getURL(),
info);
+ return connection;
} finally {
XmlaOlap4jDriver.THREAD_PROXY.set(null);
}