Skip to content

Commit

Permalink
Supply CatalogName property in an XMLA request implicitly if the CATA…
Browse files Browse the repository at this point in the history
…LOG_NAME

restriction is specified. But don't specify catalog otherwise. People expect
to be able to get, say, all cubes from all catalogs and schemas. Fixes bug
2874977, "XMLA cube discovery".

If a member is a measure, ensure that it implements Measure. To fix this,
introduce a cache of all measures. Load measures when cube is instantiated;
loading them lazily causes timing bugs, and doesn't gain us much. Fixes test
ConnectionTest.testMeasureVersusMemberCasting.

When forming a cell set, collect the unique names of members from both axes
before looking them up. Previously we would do a lookup per axis.

Reuse empty map if member has no properties.
Remove mistaken XmlaOlap4jMember.equeals method.
In comparisons with enum members, replace .equals with ==.
Add CATALOG_NAME column to calls to MDSCHEMA_ACTIONS, MDSCHEMA_DIMENSIONS.


git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@287 c6a108a4-781c-0410-a6c6-c2d559e19af0
  • Loading branch information
julianhyde committed Oct 17, 2009
1 parent 82ee139 commit 1676e91
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 107 deletions.
2 changes: 2 additions & 0 deletions src/org/olap4j/OlapDatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public interface OlapDatabaseMetaData extends DatabaseMetaData, OlapWrapper {
* <p>Specification as for XML/A MDSCHEMA_ACTIONS schema rowset.
*
* <p>Each action description has the following columns:
* <li><b>CATALOG_NAME</b> String (may be <code>null</code>) => The name of
* the database.</li>
* <li><b>SCHEMA_NAME</b> String (may be <code>null</code>) => The name of
* the schema to which this action belongs.</li>
* <li><b>CUBE_NAME</b> String => The name of the cube to which this action
Expand Down
66 changes: 36 additions & 30 deletions src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, XmlaOlap4jMember> memberMap =
new HashMap<String, XmlaOlap4jMember>();
List<String> uniqueNames = new ArrayList<String>();
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<Property, String> propertyValues =
new HashMap<Property, String>();

// 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<String, XmlaOlap4jMember> memberMap =
new HashMap<String, XmlaOlap4jMember>();
List<String> uniqueNames = new ArrayList<String>();

for (Element tupleNode
: findChildren(tuplesNode, MDDATASET_NS, "Tuple"))
{
Expand All @@ -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<Property, Object> propertyValues =
new HashMap<Property, Object>();
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"))
{
Expand Down Expand Up @@ -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<Property, Object> propertyValues =
new HashMap<Property, Object>();
final Element cellDataNode = findChild(root, MDDATASET_NS, "CellData");
for (Element cell : findChildren(cellDataNode, MDDATASET_NS, "Cell")) {
propertyValues.clear();
Expand Down
54 changes: 50 additions & 4 deletions src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ public String generateRequest(
"</RequestType>\n"
+ " <Restrictions>\n"
+ " <RestrictionList>\n");
String catalogName = null;
if (restrictions.length > 0) {
if (restrictions.length % 2 != 0) {
throw new IllegalArgumentException();
Expand All @@ -778,6 +779,11 @@ public String generateRequest(
buf.append(xmlEncode(value));
buf.append("</").append(restriction).append(">");

// To remind ourselves to generate a <Catalog> restriction
// if the request supports it.
if (restriction.equals("CATALOG_NAME")) {
catalogName = value;
}
} else {
//noinspection unchecked
List<String> valueList = (List<String>) o;
Expand All @@ -802,10 +808,30 @@ public String generateRequest(
buf.append("</DataSourceInfo>\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(" <Catalog>");
buf.append(xmlEncode(context.olap4jConnection.getCatalog()));
buf.append(xmlEncode(catalogName));
buf.append("</Catalog>\n");
}

Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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 &lt;CatalogName&gt;} 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;
}

/**
Expand Down
Loading

0 comments on commit 1676e91

Please sign in to comment.