Skip to content

Commit

Permalink
MONDRIAN/3.0: Fix bug 2031158, "SubstitutingMemberReader.getMemberBui…
Browse files Browse the repository at this point in the history
…lder gives UnsupportedOperationException". The problem was how a native-optimized crossjoin interacted with access-controlled dimension.

[git-p4: depot-paths = "//open/mondrian-release/3.0/": change = 11363]
  • Loading branch information
julianhyde committed Jul 29, 2008
1 parent 3548087 commit afe22b3
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 1 deletion.
40 changes: 39 additions & 1 deletion src/main/mondrian/rolap/SubstitutingMemberReader.java
Expand Up @@ -13,6 +13,8 @@
import mondrian.rolap.sql.TupleConstraint;

import java.util.*;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
* Implementation of {@link MemberReader} which replaces given members
Expand All @@ -26,6 +28,8 @@
* @since Oct 5, 2007
*/
public abstract class SubstitutingMemberReader extends DelegatingMemberReader {
private final TupleReader.MemberBuilder memberBuilder =
new SubstitutingMemberBuilder();

/**
* Creates a SubstitutingMemberReader.
Expand Down Expand Up @@ -184,7 +188,7 @@ public RolapMember getMemberParent(RolapMember member) {
}

public TupleReader.MemberBuilder getMemberBuilder() {
throw new UnsupportedOperationException();
return memberBuilder;
}

/**
Expand Down Expand Up @@ -218,6 +222,40 @@ public RolapMember remove(int index) {
return list.remove(index);
}
}

private class SubstitutingMemberBuilder
implements TupleReader.MemberBuilder
{
public MemberCache getMemberCache() {
return memberReader.getMemberBuilder().getMemberCache();
}

public Object getMemberCacheLock() {
return memberReader.getMemberBuilder().getMemberCacheLock();
}

public RolapMember makeMember(
RolapMember parentMember,
RolapLevel childLevel,
Object value,
Object captionValue,
boolean parentChild,
ResultSet resultSet,
Object key,
int column) throws SQLException
{
return substitute(
memberReader.getMemberBuilder().makeMember(
desubstitute(parentMember),
childLevel,
value,
captionValue,
parentChild,
resultSet,
key,
column));
}
}
}

// End SubstitutingMemberReader.java
123 changes: 123 additions & 0 deletions testsrc/main/mondrian/test/AccessControlTest.java
Expand Up @@ -13,6 +13,11 @@

import junit.framework.Assert;
import mondrian.olap.*;
import org.eigenbase.util.property.*;
import org.eigenbase.util.property.Property;

import java.util.Map;
import java.util.HashMap;

/**
* <code>AccessControlTest</code> is a set of unit-tests for access-control.
Expand All @@ -25,6 +30,8 @@
* @version $Id$
*/
public class AccessControlTest extends FoodMartTestCase {
private final PropertySaver propertySaver = new PropertySaver();

private static final String BiServer1574Role1 =
"<Role name=\"role1\">\n"
+ " <SchemaGrant access=\"none\">\n"
Expand All @@ -44,6 +51,12 @@ public AccessControlTest(String name) {
super(name);
}

protected void tearDown() throws Exception {
// revert any properties that have been set during this test
propertySaver.reset();
super.tearDown();
}

public void testGrantDimensionNone() {
final Connection connection =
getTestContext().getFoodMartConnection(false);
Expand Down Expand Up @@ -1274,6 +1287,116 @@ public void testBug2028231() {
"Row #0: 2,696.758\n" +
"Row #0: \n"));
}

/**
* Testcase for bug 2031158,
* "SubstitutingMemberReader.getMemberBuilder gives
* UnsupportedOperationException".
*/
public void testBug2031158() {
propertySaver.set(propertySaver.properties.EnableNativeCrossJoin, true);
propertySaver.set(propertySaver.properties.EnableNativeFilter, true);
propertySaver.set(propertySaver.properties.EnableNativeNonEmpty, true);
propertySaver.set(propertySaver.properties.EnableNativeTopCount, true);
propertySaver.set(propertySaver.properties.ExpandNonNative, true);

// Run with native enabled, then with whatever properties are set for
// this test run.
checkBug2031158();
propertySaver.reset();
checkBug2031158();
}

private void checkBug2031158() {
final TestContext testContext =
TestContext.create(
null, null, null, null, null, BiServer1574Role1)
.withRole("role1");

testContext.assertQueryReturns(
"select non empty {[Measures].[Units Ordered],\n"
+ " [Measures].[Units Shipped]} on 0,\n"
+ "non empty hierarchize(\n"
+ " union(\n"
+ " crossjoin(\n"
+ " {[Store Size in SQFT]},\n"
+ " {[Product].[Drink],\n"
+ " [Product].[Food],\n"
+ " [Product].[Drink].[Dairy]}),\n"
+ " crossjoin(\n"
+ " {[Store Size in SQFT].[20319]},\n"
+ " {[Product].Children}))) on 1\n"
+ "from [Warehouse]",
fold("Axis #0:\n" +
"{}\n" +
"Axis #1:\n" +
"{[Measures].[Units Ordered]}\n" +
"{[Measures].[Units Shipped]}\n" +
"Axis #2:\n" +
"{[Store Size in SQFT].[All Store Size in SQFTs], [Product].[All Products].[Drink]}\n" +
"{[Store Size in SQFT].[All Store Size in SQFTs], [Product].[All Products].[Drink].[Dairy]}\n" +
"{[Store Size in SQFT].[All Store Size in SQFTs], [Product].[All Products].[Food]}\n" +
"{[Store Size in SQFT].[All Store Size in SQFTs].[20319], [Product].[All Products].[Drink]}\n" +
"{[Store Size in SQFT].[All Store Size in SQFTs].[20319], [Product].[All Products].[Food]}\n" +
"{[Store Size in SQFT].[All Store Size in SQFTs].[20319], [Product].[All Products].[Non-Consumable]}\n" +
"Row #0: 865.0\n" +
"Row #0: 767.0\n" +
"Row #1: 195.0\n" +
"Row #1: 182.0\n" +
"Row #2: 6065.0\n" +
"Row #2: 5723.0\n" +
"Row #3: 865.0\n" +
"Row #3: 767.0\n" +
"Row #4: 6065.0\n" +
"Row #4: 5723.0\n" +
"Row #5: 2179.0\n" +
"Row #5: 2025.0\n"));
}

/**
* Sets properties, and remembers them so they can be reverted at the
* end of the test.
*/
static class PropertySaver {

public final MondrianProperties properties =
MondrianProperties.instance();

private final Map<Property, String> originalValues =
new HashMap<Property, String>();

// wacky initializer to prevent compiler from internalizing the
// string (we don't want it to be == other occurrences of "NOT_SET")
private static final String NOT_SET =
new StringBuffer("NOT_" + "SET").toString();

// need to implement for other kinds of property too
public void set(BooleanProperty property, boolean value) {
if (!originalValues.containsKey(property)) {
final String originalValue =
properties.containsKey(property.getPath())
? properties.getProperty(property.getPath())
: NOT_SET;
originalValues.put(
property,
originalValue);
}
property.set(value);
}

public void reset() {
for (Map.Entry<Property,String> entry : originalValues.entrySet())
{
final String value = entry.getValue();
//noinspection StringEquality
if (value == NOT_SET) {
properties.remove(entry.getKey());
} else {
properties.setProperty(entry.getKey().getPath(), value);
}
}
}
}
}

// End AccessControlTest.java

0 comments on commit afe22b3

Please sign in to comment.