diff --git a/src/org/olap4j/OlapDatabaseMetaData.java b/src/org/olap4j/OlapDatabaseMetaData.java
index 38b8f07..e9a69c9 100644
--- a/src/org/olap4j/OlapDatabaseMetaData.java
+++ b/src/org/olap4j/OlapDatabaseMetaData.java
@@ -44,6 +44,8 @@ public interface OlapDatabaseMetaData extends DatabaseMetaData, OlapWrapper {
*
Specification as for XML/A MDSCHEMA_ACTIONS schema rowset.
*
*
Each action description has the following columns:
+ *
CATALOG_NAME String (may be null
) => The name of
+ * the database.
* SCHEMA_NAME String (may be null
) => The name of
* the schema to which this action belongs.
* CUBE_NAME String => The name of the cube to which this action
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
index e96de07..bfbb106 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
@@ -182,34 +182,23 @@ void populate() throws OlapException {
*/
final Element axesNode = findChild(root, MDDATASET_NS, "Axes");
+
+ // 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 axisNode : findChildren(axesNode, MDDATASET_NS, "Axis")) {
- final String axisName = axisNode.getAttribute("name");
- final Axis axis = lookupAxis(axisName);
- final XmlaOlap4jCellSetAxis cellSetAxis =
- new XmlaOlap4jCellSetAxis(this, axis);
- if (axis.isFilter()) {
- filterAxis = cellSetAxis;
- } else {
- axisList.add(cellSetAxis);
- }
final Element tuplesNode =
findChild(axisNode, MDDATASET_NS, "Tuples");
- 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"))
{
@@ -220,9 +209,27 @@ void populate() throws OlapException {
uniqueNames.add(uname);
}
}
- metadataReader.lookupMembersByUniqueName(uniqueNames, memberMap);
+ }
- // Second pass, populate the axis.
+ // Fetch all members on all axes. Hopefully it can all be done in one
+ // round trip, or they are in cache already.
+ metadataReader.lookupMembersByUniqueName(uniqueNames, memberMap);
+
+ // Second pass, populate the axis.
+ final Map propertyValues =
+ new HashMap();
+ for (Element axisNode : findChildren(axesNode, MDDATASET_NS, "Axis")) {
+ final String axisName = axisNode.getAttribute("name");
+ final Axis axis = lookupAxis(axisName);
+ final XmlaOlap4jCellSetAxis cellSetAxis =
+ new XmlaOlap4jCellSetAxis(this, axis);
+ if (axis.isFilter()) {
+ filterAxis = cellSetAxis;
+ } else {
+ axisList.add(cellSetAxis);
+ }
+ final Element tuplesNode =
+ findChild(axisNode, MDDATASET_NS, "Tuples");
for (Element tupleNode
: findChildren(tuplesNode, MDDATASET_NS, "Tuple"))
{
@@ -264,12 +271,11 @@ void populate() throws OlapException {
members.add(member);
}
cellSetAxis.positions.add(
- new XmlaOlap4jPosition(members, ordinal++));
+ new XmlaOlap4jPosition(
+ members, cellSetAxis.positions.size()));
}
}
- final Map propertyValues =
- new HashMap();
final Element cellDataNode = findChild(root, MDDATASET_NS, "CellData");
for (Element cell : findChildren(cellDataNode, MDDATASET_NS, "Cell")) {
propertyValues.clear();
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
index df22b28..1fdab79 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
@@ -765,6 +765,7 @@ public String generateRequest(
"\n"
+ " \n"
+ " \n");
+ String catalogName = null;
if (restrictions.length > 0) {
if (restrictions.length % 2 != 0) {
throw new IllegalArgumentException();
@@ -778,6 +779,11 @@ public String generateRequest(
buf.append(xmlEncode(value));
buf.append("").append(restriction).append(">");
+ // To remind ourselves to generate a restriction
+ // if the request supports it.
+ if (restriction.equals("CATALOG_NAME")) {
+ catalogName = value;
+ }
} else {
//noinspection unchecked
List valueList = (List) o;
@@ -802,10 +808,30 @@ public String generateRequest(
buf.append("\n");
}
- // Add the catalog node only if this request requires it.
- if (metadataRequest.requiresCatalogName()) {
+ // If the request requires catalog name, and one wasn't specified in the
+ // restrictions, use the connection's current catalog.
+ if (catalogName == null
+ && metadataRequest.requiresCatalogName())
+ {
+ catalogName = context.olap4jConnection.getCatalog();
+ }
+
+ // Add the catalog node only if this request has specified it as a
+ // restriction.
+ //
+ // For low-level objects like cube, the restriction is optional; you can
+ // specify null to not restrict, "" to match cubes whose catalog name is
+ // empty, or a string (not interpreted as a wild card). (See
+ // OlapDatabaseMetaData.getCubes API doc for more details.) We assume
+ // that the request provides the restriction only if it is valid.
+ //
+ // For high level objects like data source and catalog, the catalog
+ // restriction does not make sense.
+ if (catalogName != null
+ && metadataRequest.allowsCatalogName())
+ {
buf.append(" ");
- buf.append(xmlEncode(context.olap4jConnection.getCatalog()));
+ buf.append(xmlEncode(catalogName));
buf.append("\n");
}
@@ -1819,6 +1845,7 @@ enum MetadataRequest {
new MetadataColumn("SCHEMA_NAME"),
new MetadataColumn("SCHEMA_OWNER")),
MDSCHEMA_ACTIONS(
+ new MetadataColumn("CATALOG_NAME"),
new MetadataColumn("SCHEMA_NAME"),
new MetadataColumn("CUBE_NAME"),
new MetadataColumn("ACTION_NAME"),
@@ -2009,7 +2036,26 @@ public boolean requiresDatasourceName() {
* @return whether this request requires a CatalogName element
*/
public boolean requiresCatalogName() {
- return (this != DBSCHEMA_CATALOGS && this != DISCOVER_DATASOURCES);
+ // If we don't specifiy CatalogName in the properties of an
+ // MDSCHEMA_FUNCTIONS request, Mondrian's XMLA provider will give
+ // us the whole set of functions multiplied by the number of
+ // catalogs. JDBC (and Mondrian) assumes that functions belong to a
+ // catalog whereas XMLA (and SSAS) assume that functions belong to
+ // the database. Always specifying a catalog is the easiest way to
+ // reconcile them.
+ return this == MDSCHEMA_FUNCTIONS;
+ }
+
+ /**
+ * Returns whether this request allows a
+ * {@code <CatalogName>} element in the properties section of the
+ * request. Even for requests that allow it, it is usually optional.
+ *
+ * @return whether this request allows a CatalogName element
+ */
+ public boolean allowsCatalogName() {
+ return this != DBSCHEMA_CATALOGS
+ && this != DISCOVER_DATASOURCES;
}
/**
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java
index 365c483..a26c883 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java
@@ -38,7 +38,10 @@ class XmlaOlap4jCube implements Cube, Named
new HashMap();
final Map levelsByUname =
new HashMap();
- final NamedList measures;
+ final List measures =
+ new ArrayList();
+ private final HashMap measuresMap =
+ new HashMap();
private final NamedList namedSets =
new NamedListImpl();
private final MetadataReader metadataReader;
@@ -63,7 +66,8 @@ class XmlaOlap4jCube implements Cube, Named
this.description = description;
this.metadataReader =
new CachingMetadataReader(
- new RawMetadataReader());
+ new RawMetadataReader(),
+ measuresMap);
final XmlaOlap4jConnection olap4jConnection =
olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
@@ -78,32 +82,26 @@ class XmlaOlap4jCube implements Cube, Named
this.dimensions = new DeferredNamedListImpl(
XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_DIMENSIONS,
- new XmlaOlap4jConnection.Context(
- olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData
- .olap4jConnection,
- olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData,
- olap4jSchema.olap4jCatalog,
- olap4jSchema,
- this, null, null, null),
- new XmlaOlap4jConnection.DimensionHandler(this),
- restrictions);
-
- this.measures = new DeferredNamedListImpl(
+ context,
+ new XmlaOlap4jConnection.DimensionHandler(this),
+ restrictions);
+
+ // populate measures up front; a measure is needed in every query
+ olap4jConnection.populateList(
+ measures,
+ context,
XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_MEASURES,
- new XmlaOlap4jConnection.Context(
- olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData
- .olap4jConnection,
- olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData,
- olap4jSchema.olap4jCatalog,
- olap4jSchema,
- this, null, null, null),
- new XmlaOlap4jConnection.MeasureHandler(
- this.dimensions.get("Measures")),
- restrictions);
+ new XmlaOlap4jConnection.MeasureHandler(
+ this.dimensions.get("Measures")),
+ restrictions);
+ for (XmlaOlap4jMeasure measure : measures) {
+ measuresMap.put(measure.getUniqueName(), measure);
+ }
// populate named sets
olap4jConnection.populateList(
- namedSets, context,
+ namedSets,
+ context,
XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_SETS,
new XmlaOlap4jConnection.NamedSetHandler(),
restrictions);
@@ -136,7 +134,7 @@ public NamedList getDimensions() {
public NamedList getHierarchies() {
// This is a costly operation. It forces the init
// of all dimensions and all hierarchies.
- // We differ it to this point.
+ // We defer it to this point.
if (this.hierarchies == null) {
this.hierarchies = new NamedListImpl();
for (XmlaOlap4jDimension dim : this.dimensions) {
@@ -278,6 +276,8 @@ public List getLevelMembers(
private static class CachingMetadataReader
extends DelegatingMetadataReader
{
+ private final Map measuresMap;
+
private final Map> memberMap =
new HashMap>();
@@ -292,14 +292,28 @@ private static class CachingMetadataReader
* Creates a CachingMetadataReader.
*
* @param metadataReader Underlying metadata reader
+ * @param measuresMap Map of measures by unique name, inherited from the
+ * cube and used read-only by this reader
*/
- CachingMetadataReader(MetadataReader metadataReader) {
+ CachingMetadataReader(
+ MetadataReader metadataReader,
+ Map measuresMap)
+ {
super(metadataReader);
+ this.measuresMap = measuresMap;
}
public XmlaOlap4jMember lookupMemberByUniqueName(
String memberUniqueName) throws OlapException
{
+ // First, look in measures map.
+ XmlaOlap4jMeasure measure =
+ measuresMap.get(memberUniqueName);
+ if (measure != null) {
+ return measure;
+ }
+
+ // Next, look in cache.
final SoftReference memberRef =
memberMap.get(memberUniqueName);
if (memberRef != null) {
@@ -308,11 +322,11 @@ public XmlaOlap4jMember lookupMemberByUniqueName(
return member;
}
}
+
final XmlaOlap4jMember member =
super.lookupMemberByUniqueName(memberUniqueName);
if (member != null
- && !member.getDimension()
- .type.equals(Dimension.Type.MEASURE))
+ && member.getDimension().type != Dimension.Type.MEASURE)
{
memberMap.put(
memberUniqueName,
@@ -328,6 +342,15 @@ public void lookupMembersByUniqueName(
final ArrayList remainingMemberUniqueNames =
new ArrayList();
for (String memberUniqueName : memberUniqueNames) {
+ // First, look in measures map.
+ XmlaOlap4jMeasure measure =
+ measuresMap.get(memberUniqueName);
+ if (measure != null) {
+ memberMap.put(memberUniqueName, measure);
+ continue;
+ }
+
+ // Next, look in cache.
final SoftReference memberRef =
this.memberMap.get(memberUniqueName);
final XmlaOlap4jMember member;
@@ -335,10 +358,12 @@ public void lookupMembersByUniqueName(
&& (member = memberRef.get()) != null)
{
memberMap.put(memberUniqueName, member);
- } else {
- remainingMemberUniqueNames.add(memberUniqueName);
+ continue;
}
+
+ remainingMemberUniqueNames.add(memberUniqueName);
}
+
// If any of the member names were not in the cache, look them up
// by delegating.
if (!remainingMemberUniqueNames.isEmpty()) {
@@ -350,8 +375,8 @@ public void lookupMembersByUniqueName(
XmlaOlap4jMember member = memberMap.get(memberName);
if (member != null) {
if (!(member instanceof Measure)
- && !(member.getDimension().type
- .equals(Dimension.Type.MEASURE)))
+ && member.getDimension().type
+ != Dimension.Type.MEASURE)
{
this.memberMap.put(
memberName,
@@ -376,8 +401,8 @@ public List getLevelMembers(
}
final List memberList =
super.getLevelMembers(level);
- if (!level.olap4jHierarchy.olap4jDimension.type
- .equals(Dimension.Type.MEASURE))
+ if (level.olap4jHierarchy.olap4jDimension.type
+ != Dimension.Type.MEASURE)
{
levelMemberListMap.put(
level,
@@ -421,20 +446,22 @@ public void lookupMembersByUniqueName(
.olap4jConnection.getDataSourceInfo()
.indexOf("Provider=Mondrian") != -1) //$NON-NLS-1$
{
- memberMap.putAll(this.mondrianMembersLookup(memberUniqueNames));
+ mondrianMembersLookup(memberUniqueNames, memberMap);
} else {
- memberMap.putAll(this.genericMembersLookup(memberUniqueNames));
+ genericMembersLookup(memberUniqueNames, memberMap);
}
}
/**
- * This is an optimized method for Mondrian servers members lookup.
+ * Looks up members; optimized for Mondrian servers.
+ *
* @param memberUniqueNames A list of the members to lookup
- * @return A map of members with their unique name as a key
+ * @param memberMap Output map of members keyed by unique name
* @throws OlapException Gets thrown for communication errors
*/
- private Map mondrianMembersLookup(
- List memberUniqueNames) throws OlapException
+ private void mondrianMembersLookup(
+ List memberUniqueNames,
+ Map memberMap) throws OlapException
{
final XmlaOlap4jConnection.Context context =
new XmlaOlap4jConnection.Context(
@@ -453,27 +480,24 @@ private Map mondrianMembersLookup(
"CUBE_NAME", getName(),
"MEMBER_UNIQUE_NAME", memberUniqueNames
});
- final Map memberMap =
- new HashMap(memberUniqueNames.size());
for (XmlaOlap4jMember member : memberList) {
if (member != null) {
memberMap.put(member.getUniqueName(), member);
}
}
- return memberMap;
}
/**
- * This is an generic method for members lookup.
+ * Looks up members.
+ *
* @param memberUniqueNames A list of the members to lookup
- * @return A map of members with their unique name as a key
+ * @param memberMap Output map of members keyed by unique name
* @throws OlapException Gets thrown for communication errors
*/
- private Map genericMembersLookup(
- List memberUniqueNames) throws OlapException
+ private void genericMembersLookup(
+ List memberUniqueNames,
+ Map memberMap) throws OlapException
{
- final Map memberMap =
- new HashMap(memberUniqueNames.size());
// Iterates through member names
for (String currentMemberName : memberUniqueNames) {
// Only lookup if it is not in the map yet
@@ -486,7 +510,6 @@ private Map genericMembersLookup(
}
}
}
- return memberMap;
}
public void lookupMemberRelatives(
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java b/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java
index 4180486..48699e5 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java
@@ -967,6 +967,7 @@ public ResultSet getActions(
{
return getMetadata(
XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_ACTIONS,
+ "CATALOG_NAME", catalog,
"SCHEMA_NAME", wildcard(schemaPattern),
"CUBE_NAME", wildcard(cubeNamePattern),
"ACTION_NAME", wildcard(actionNamePattern));
@@ -1056,6 +1057,7 @@ public ResultSet getDimensions(
{
return getMetadata(
XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_DIMENSIONS,
+ "CATALOG_NAME", catalog,
"SCHEMA_NAME", wildcard(schemaPattern),
"CUBE_NAME", wildcard(cubeNamePattern),
"DIMENSION_NAME", wildcard(dimensionNamePattern));
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java b/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java
index 1eab351..ed3f0ee 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jLevel.java
@@ -90,7 +90,7 @@ class XmlaOlap4jLevel
try {
if (olap4jHierarchy.olap4jDimension.getDimensionType()
- .equals(Dimension.Type.MEASURE))
+ == Dimension.Type.MEASURE)
{
String[] restrictions = {
"CATALOG_NAME",
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java b/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java
index 64338bf..0eae6b5 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java
@@ -29,6 +29,20 @@ class XmlaOlap4jMeasure
private final Datatype datatype;
private final boolean visible;
+ /**
+ * Creates an XmlaOlap4jMeasure.
+ *
+ * @param olap4jLevel Level
+ * @param uniqueName Unique name
+ * @param name Name
+ * @param caption Caption
+ * @param description Description
+ * @param parentMemberUniqueName Unique name of parent, or null if no parent
+ * @param aggregator Aggregator
+ * @param datatype Data type
+ * @param visible Whether visible
+ * @param ordinal Ordinal in its hierarchy
+ */
XmlaOlap4jMeasure(
XmlaOlap4jLevel olap4jLevel,
String uniqueName,
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java b/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java
index dc18356..65194a5 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jMember.java
@@ -45,8 +45,22 @@ class XmlaOlap4jMember
private XmlaOlap4jMember parentMember;
private final int childMemberCount;
private final int ordinal;
- private final ArrayMap propertyValueMap;
+ private final Map propertyValueMap;
+ /**
+ * Creates an XmlaOlap4jMember.
+ *
+ * @param olap4jLevel Level
+ * @param uniqueName Unique name
+ * @param name Name
+ * @param caption Caption
+ * @param description Description
+ * @param parentMemberUniqueName Unique name of parent, or null if no parent
+ * @param type Type
+ * @param childMemberCount Number of children
+ * @param ordinal Ordinal in its hierarchy
+ * @param propertyValueMap Property values
+ */
XmlaOlap4jMember(
XmlaOlap4jLevel olap4jLevel,
String uniqueName,
@@ -67,8 +81,12 @@ class XmlaOlap4jMember
this.parentMemberUniqueName = parentMemberUniqueName;
this.type = type;
this.childMemberCount = childMemberCount;
- this.propertyValueMap =
- new ArrayMap(propertyValueMap);
+ if (propertyValueMap.isEmpty()) {
+ this.propertyValueMap = Collections.emptyMap();
+ } else {
+ this.propertyValueMap =
+ new ArrayMap(propertyValueMap);
+ }
}
public int hashCode() {
@@ -317,12 +335,6 @@ static int toInteger(Object o) {
public Member getDataMember() {
throw new UnsupportedOperationException();
}
-
- public boolean equeals(Object obj) {
- return (obj instanceof XmlaOlap4jMember)
- && this.uniqueName.equals(
- ((XmlaOlap4jMember) obj).getUniqueName());
- }
}
// End XmlaOlap4jMember.java
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jPositionMember.java b/src/org/olap4j/driver/xmla/XmlaOlap4jPositionMember.java
index 2e7875a..e192462 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jPositionMember.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jPositionMember.java
@@ -35,7 +35,7 @@ class XmlaOlap4jPositionMember
implements XmlaOlap4jMemberBase
{
private final XmlaOlap4jMemberBase member;
- private final Map propertyValues;
+ private final Map propertyValues;
/**
* Creates a XmlaOlap4jPositionMember.
@@ -45,12 +45,12 @@ class XmlaOlap4jPositionMember
*/
XmlaOlap4jPositionMember(
XmlaOlap4jMemberBase member,
- Map propertyValues)
+ Map propertyValues)
{
assert member != null;
assert propertyValues != null;
this.member = member;
- this.propertyValues = new ArrayMap(propertyValues);
+ this.propertyValues = new ArrayMap(propertyValues);
}
public boolean equals(Object obj) {
@@ -156,7 +156,7 @@ public String getPropertyFormattedValue(Property property) {
// come back as part of axis tuple. Unformatted property is best we
// can do.
if (propertyValues.containsKey(property)) {
- return propertyValues.get(property);
+ return String.valueOf(propertyValues.get(property));
}
return member.getPropertyFormattedValue(property);
}
diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java
index ba78698..a138adb 100644
--- a/testsrc/org/olap4j/ConnectionTest.java
+++ b/testsrc/org/olap4j/ConnectionTest.java
@@ -533,6 +533,7 @@ public void testMeasureVersusMemberCasting() throws Exception {
columnsMember.getUniqueName(),
!(columnsMember instanceof Measure));
}
+
public void testInvalidStatement() throws SQLException {
connection = tester.createConnection();
Statement statement = connection.createStatement();
@@ -1557,7 +1558,10 @@ public void testMetadata() throws Exception {
// Schema
boolean found = false;
for (Catalog catalog : olapConnection.getCatalogs()) {
+ assertSame(olapConnection.getMetaData(), catalog.getMetaData());
+ assertNotNull(catalog.getName());
for (Schema schema : catalog.getSchemas()) {
+ assertSame(schema.getCatalog(), catalog);
if (schema.equals(olapConnection.getSchema())) {
found = true;
break;
@@ -1566,6 +1570,16 @@ public void testMetadata() throws Exception {
}
assertTrue(found);
+ // We engineered the XMLA test environment to have two catalogs.
+ switch (tester.getFlavor()) {
+ case XMLA:
+ assertEquals(2, olapConnection.getCatalogs().size());
+ break;
+ case MONDRIAN:
+ assertEquals(1, olapConnection.getCatalogs().size());
+ break;
+ }
+
Cube cube = olapConnection.getSchema().getCubes().get("Sales");
int z = 0;
@@ -2149,9 +2163,11 @@ public void testValidateError() throws Exception {
fail("expected parse error, got " + select);
} catch (OlapException e) {
assertEquals("Validation error", e.getMessage());
+ final String stackTrace = TestContext.getStackTrace(e);
assertTrue(
- TestContext.getStackTrace(e).contains(
- "Dimension '[Gender]' appears in more than one "
+ stackTrace,
+ stackTrace.contains(
+ "Hierarchy '[Gender]' appears in more than one "
+ "independent axis."));
}
}
diff --git a/testsrc/org/olap4j/MetadataTest.java b/testsrc/org/olap4j/MetadataTest.java
index dcd4b3f..c3f171a 100644
--- a/testsrc/org/olap4j/MetadataTest.java
+++ b/testsrc/org/olap4j/MetadataTest.java
@@ -94,7 +94,7 @@ public class MetadataTest extends TestCase {
private static final List SCHEMAS_COLUMN_NAMES = Arrays.asList(
"TABLE_SCHEM", "TABLE_CAT");
private static final List ACTIONS_COLUMN_NAMES = Arrays.asList(
- "SCHEMA_NAME", "CUBE_NAME", "ACTION_NAME", "COORDINATE",
+ "CATALOG_NAME", "SCHEMA_NAME", "CUBE_NAME", "ACTION_NAME", "COORDINATE",
"COORDINATE_TYPE");
public MetadataTest() throws SQLException {
@@ -274,18 +274,34 @@ public void testDatabaseMetaDataGetCatalogs() throws SQLException {
String s = checkResultSet(
olapDatabaseMetaData.getCatalogs(),
CATALOGS_COLUMN_NAMES);
- TestContext.assertEqualsVerbose(
- "TABLE_CAT=" + catalogName + "\n",
- s);
+ final String expected;
+ if (tester.getFlavor() == TestContext.Tester.Flavor.XMLA) {
+ // XMLA test uses dummy duplicate catalog to make sure that we
+ // get all catalogs
+ expected =
+ "TABLE_CAT=" + catalogName + "\n"
+ + "TABLE_CAT=" + catalogName + "2\n";
+ } else {
+ expected = "TABLE_CAT=" + catalogName + "\n";
+ }
+ TestContext.assertEqualsVerbose(expected, s);
}
public void testDatabaseMetaDataGetSchemas() throws SQLException {
String s = checkResultSet(
olapDatabaseMetaData.getSchemas(),
SCHEMAS_COLUMN_NAMES);
- TestContext.assertEqualsVerbose(
- "TABLE_SCHEM=FoodMart, TABLE_CAT=" + catalogName + "\n",
- s);
+ final String expected;
+ if (tester.getFlavor() == TestContext.Tester.Flavor.XMLA) {
+ // XMLA test uses dummy duplicate catalog to make sure that we
+ // get all catalogs
+ expected =
+ "TABLE_SCHEM=FoodMart, TABLE_CAT=" + catalogName + "\n"
+ + "TABLE_SCHEM=FoodMart, TABLE_CAT=" + catalogName + "2\n";
+ } else {
+ expected = "TABLE_SCHEM=FoodMart, TABLE_CAT=" + catalogName + "\n";
+ }
+ TestContext.assertEqualsVerbose(expected, s);
}
public void testDatabaseMetaDataGetLiterals() throws SQLException {
@@ -350,6 +366,25 @@ public void testDatabaseMetaDataGetCubes() throws SQLException {
"CATALOG_NAME=" + catalogName
+ ", SCHEMA_NAME=FoodMart, CUBE_NAME=Sales, ",
s);
+ final int lineCount = linecount(s);
+
+ // again, but with null catalog name. should yield twice as many
+ // cubes on xmla, where we have two identical catalogs
+ s = checkResultSet(
+ olapDatabaseMetaData.getCubes(
+ catalogName,
+ null,
+ null),
+ CUBE_COLUMN_NAMES);
+ assertContains(
+ "CATALOG_NAME=" + catalogName
+ + ", SCHEMA_NAME=FoodMart, CUBE_NAME=Sales, ",
+ s);
+ final int lineCount2 = linecount(s);
+
+ if (tester.getFlavor() == TestContext.Tester.Flavor.XMLA) {
+ assertEquals(lineCount2, lineCount * 2);
+ }
s = checkResultSet(
olapDatabaseMetaData.getCubes(
diff --git a/testsrc/org/olap4j/XmlaTester.java b/testsrc/org/olap4j/XmlaTester.java
index 45b210a..b9b8beb 100644
--- a/testsrc/org/olap4j/XmlaTester.java
+++ b/testsrc/org/olap4j/XmlaTester.java
@@ -47,9 +47,19 @@ public XmlaTester()
final String catalogUrl =
properties.getProperty(
TestContext.Property.XMLA_CATALOG_URL.path);
+ if (catalogUrl == null) {
+ throw new RuntimeException(
+ "Property " + TestContext.Property.XMLA_CATALOG_URL.path
+ + " must be specified");
+ }
+
+ // Include the same catalog URL twice with different catalog names. This
+ // allows us to detect whether operations are restricting to the current
+ // catalog. (Some should, most should not.)
Map catalogNameUrls =
new HashMap();
catalogNameUrls.put("FoodMart", catalogUrl);
+ catalogNameUrls.put("FoodMart2", catalogUrl);
String urlString =
properties.getProperty(TestContext.Property.CONNECT_URL.path);