001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2007-2012 Pentaho and others 008// All Rights Reserved. 009*/ 010package mondrian.olap4j; 011 012import mondrian.mdx.*; 013import mondrian.olap.*; 014import mondrian.olap.Member; 015import mondrian.olap.fun.MondrianEvaluationException; 016import mondrian.rolap.*; 017import mondrian.util.Bug; 018import mondrian.xmla.XmlaHandler; 019 020import org.olap4j.Axis; 021import org.olap4j.Cell; 022import org.olap4j.*; 023import org.olap4j.impl.*; 024import org.olap4j.mdx.*; 025import org.olap4j.mdx.parser.*; 026import org.olap4j.mdx.parser.impl.DefaultMdxParserImpl; 027import org.olap4j.metadata.*; 028import org.olap4j.metadata.Database.AuthenticationMode; 029import org.olap4j.metadata.Database.ProviderType; 030import org.olap4j.metadata.Schema; 031import org.olap4j.type.*; 032import org.olap4j.type.DimensionType; 033 034import java.io.PrintWriter; 035import java.io.StringWriter; 036import java.math.BigDecimal; 037import java.math.BigInteger; 038import java.sql.*; 039import java.util.*; 040 041/** 042 * Implementation of {@link org.olap4j.OlapConnection} 043 * for the Mondrian OLAP engine. 044 * 045 * <p>This class has sub-classes which implement JDBC 3.0 and JDBC 4.0 APIs; 046 * it is instantiated using 047 * {@link Factory#newConnection(MondrianOlap4jDriver, String, java.util.Properties)}.</p> 048 * 049 * <p>This class is public, to allow access to the 050 * {@link #setRoleNames(java.util.List)} method before it is added to olap4j 051 * version 2.0. <b>This may change without notice</b>. Code should not rely on 052 * this class being public.</p> 053 * 054 * @author jhyde 055 * @since May 23, 2007 056 */ 057public abstract class MondrianOlap4jConnection implements OlapConnection { 058 static { 059 Bug.olap4jUpgrade( 060 "Make this class package-protected when we upgrade to olap4j 2.0. " 061 + "The setRoleNames method will then be available through the " 062 + "olap4j API"); 063 } 064 065 /** 066 * Handler for errors. 067 */ 068 final Helper helper = new Helper(); 069 070 /** 071 * Underlying mondrian connection. Set on creation, cleared on close. 072 * Developers, please keep this member private. Access it via 073 * {@link #getMondrianConnection()} or {@link #getMondrianConnection2()}, 074 * and these will throw if the connection has been closed. 075 */ 076 private RolapConnection mondrianConnection; 077 078 /** 079 * Map from mondrian schema objects to olap4j schemas. 080 * 081 * <p>REVIEW: This assumes that a RolapSchema occurs at most once in a 082 * catalog. It is possible for a schema to be mapped more than once, with 083 * different names; the same RolapSchema object will be used. 084 */ 085 final Map<mondrian.olap.Schema, MondrianOlap4jSchema> schemaMap = 086 new HashMap<mondrian.olap.Schema, MondrianOlap4jSchema>(); 087 088 private final MondrianOlap4jDatabaseMetaData olap4jDatabaseMetaData; 089 090 private static final String CONNECT_STRING_PREFIX = "jdbc:mondrian:"; 091 092 private static final String ENGINE_CONNECT_STRING_PREFIX = 093 "jdbc:mondrian:engine:"; 094 095 final Factory factory; 096 final MondrianOlap4jDriver driver; 097 private String roleName; 098 099 /** List of role names. Empty if role is the 'all' role. Value must always 100 * be an unmodifiable list, because {@link #getRoleNames()} returns the 101 * value directly. */ 102 private List<String> roleNames = Collections.emptyList(); 103 private boolean autoCommit; 104 private boolean readOnly; 105 boolean preferList; 106 107 final MondrianServer mondrianServer; 108 private final MondrianOlap4jSchema olap4jSchema; 109 private final NamedList<MondrianOlap4jDatabase> olap4jDatabases; 110 111 /** 112 * Creates an Olap4j connection to Mondrian. 113 * 114 * <p>This method is intentionally package-protected. The public API 115 * uses the traditional JDBC {@link java.sql.DriverManager}. 116 * See {@link mondrian.olap4j.MondrianOlap4jDriver} for more details. 117 * 118 * @param factory Factory 119 * @param driver Driver 120 * @param url Connect-string URL 121 * @param info Additional properties 122 * @throws SQLException if there is an error 123 */ 124 MondrianOlap4jConnection( 125 Factory factory, 126 MondrianOlap4jDriver driver, 127 String url, 128 Properties info) 129 throws SQLException 130 { 131 // Required for the logic below to work. 132 assert ENGINE_CONNECT_STRING_PREFIX.startsWith(CONNECT_STRING_PREFIX); 133 134 this.factory = factory; 135 this.driver = driver; 136 String x; 137 if (url.startsWith(ENGINE_CONNECT_STRING_PREFIX)) { 138 x = url.substring(ENGINE_CONNECT_STRING_PREFIX.length()); 139 } else if (url.startsWith(CONNECT_STRING_PREFIX)) { 140 x = url.substring(CONNECT_STRING_PREFIX.length()); 141 } else { 142 // This is not a URL we can handle. 143 // DriverManager should not have invoked us. 144 throw new AssertionError( 145 "does not start with '" + CONNECT_STRING_PREFIX + "'"); 146 } 147 Util.PropertyList list = Util.parseConnectString(x); 148 final Map<String, String> map = Util.toMap(info); 149 for (Map.Entry<String, String> entry : map.entrySet()) { 150 list.put(entry.getKey(), entry.getValue()); 151 } 152 153 this.mondrianConnection = 154 (RolapConnection) mondrian.olap.DriverManager 155 .getConnection(list, null); 156 157 this.olap4jDatabaseMetaData = 158 factory.newDatabaseMetaData(this, mondrianConnection); 159 160 161 this.mondrianServer = 162 MondrianServer.forConnection(mondrianConnection); 163 final CatalogFinder catalogFinder = 164 (CatalogFinder) mondrianServer; 165 166 NamedList<MondrianOlap4jCatalog> olap4jCatalogs = new 167 NamedListImpl<MondrianOlap4jCatalog>(); 168 this.olap4jDatabases = 169 new NamedListImpl<MondrianOlap4jDatabase>(); 170 171 List<Map<String, Object>> dbpropsMaps = 172 mondrianServer.getDatabases(mondrianConnection); 173 if (dbpropsMaps.size() != 1) { 174 throw new AssertionError(); 175 } 176 Map<String, Object> dbpropsMap = dbpropsMaps.get(0); 177 StringTokenizer st = 178 new StringTokenizer( 179 String.valueOf(dbpropsMap.get("ProviderType")), 180 ","); 181 List<ProviderType> pTypes = 182 new ArrayList<ProviderType>(); 183 while (st.hasMoreTokens()) { 184 pTypes.add(ProviderType.valueOf(st.nextToken())); 185 } 186 st = new StringTokenizer( 187 String.valueOf(dbpropsMap.get("AuthenticationMode")), ","); 188 List<AuthenticationMode> aModes = 189 new ArrayList<AuthenticationMode>(); 190 while (st.hasMoreTokens()) { 191 aModes.add(AuthenticationMode.valueOf(st.nextToken())); 192 } 193 final MondrianOlap4jDatabase database = 194 new MondrianOlap4jDatabase( 195 this, 196 olap4jCatalogs, 197 String.valueOf(dbpropsMap.get("DataSourceName")), 198 String.valueOf(dbpropsMap.get("DataSourceDescription")), 199 String.valueOf(dbpropsMap.get("ProviderName")), 200 String.valueOf(dbpropsMap.get("URL")), 201 String.valueOf(dbpropsMap.get("DataSourceInfo")), 202 pTypes, 203 aModes); 204 this.olap4jDatabases.add(database); 205 206 for (String catalogName 207 : catalogFinder.getCatalogNames(mondrianConnection)) 208 { 209 final Map<String, RolapSchema> schemaMap = 210 catalogFinder.getRolapSchemas( 211 mondrianConnection, 212 catalogName); 213 olap4jCatalogs.add( 214 new MondrianOlap4jCatalog( 215 olap4jDatabaseMetaData, 216 catalogName, 217 database, 218 schemaMap)); 219 } 220 221 this.olap4jSchema = toOlap4j(mondrianConnection.getSchema()); 222 } 223 224 static boolean acceptsURL(String url) { 225 return url.startsWith(CONNECT_STRING_PREFIX); 226 } 227 228 public OlapStatement createStatement() { 229 final MondrianOlap4jStatement statement = 230 factory.newStatement(this); 231 mondrianServer.addStatement(statement); 232 return statement; 233 } 234 235 public ScenarioImpl createScenario() throws OlapException { 236 return getMondrianConnection().createScenario(); 237 } 238 239 public void setScenario(Scenario scenario) throws OlapException { 240 getMondrianConnection().setScenario(scenario); 241 } 242 243 public Scenario getScenario() throws OlapException { 244 return getMondrianConnection().getScenario(); 245 } 246 247 public PreparedStatement prepareStatement(String sql) throws SQLException { 248 throw new UnsupportedOperationException(); 249 } 250 251 public CallableStatement prepareCall(String sql) throws SQLException { 252 throw new UnsupportedOperationException(); 253 } 254 255 public String nativeSQL(String sql) throws SQLException { 256 throw new UnsupportedOperationException(); 257 } 258 259 public void setAutoCommit(boolean autoCommit) throws SQLException { 260 this.autoCommit = autoCommit; 261 } 262 263 public boolean getAutoCommit() throws SQLException { 264 return autoCommit; 265 } 266 267 public void commit() throws SQLException { 268 throw new UnsupportedOperationException(); 269 } 270 271 public void rollback() throws SQLException { 272 throw new UnsupportedOperationException(); 273 } 274 275 public void close() throws SQLException { 276 if (mondrianConnection != null) { 277 RolapConnection c = mondrianConnection; 278 mondrianConnection = null; 279 c.close(); 280 } 281 } 282 283 public boolean isClosed() throws SQLException { 284 return mondrianConnection == null; 285 } 286 287 public OlapDatabaseMetaData getMetaData() { 288 return olap4jDatabaseMetaData; 289 } 290 291 public void setReadOnly(boolean readOnly) throws SQLException { 292 this.readOnly = readOnly; 293 } 294 295 public boolean isReadOnly() throws SQLException { 296 return readOnly; 297 } 298 299 public void setSchema(String schemaName) throws OlapException { 300 // no op. 301 } 302 303 public String getSchema() throws OlapException { 304 return olap4jSchema.getName(); 305 } 306 307 public Schema getOlapSchema() throws OlapException { 308 return olap4jSchema; 309 } 310 311 public NamedList<Schema> getOlapSchemas() throws OlapException { 312 return getOlapCatalog().getSchemas(); 313 } 314 315 public void setCatalog(String catalogName) throws OlapException { 316 // no op 317 } 318 319 public String getCatalog() throws OlapException { 320 return olap4jSchema.olap4jCatalog.getName(); 321 } 322 323 public Catalog getOlapCatalog() throws OlapException { 324 return olap4jSchema.olap4jCatalog; 325 } 326 327 public NamedList<Catalog> getOlapCatalogs() throws OlapException { 328 return getOlapDatabase().getCatalogs(); 329 } 330 331 public void setDatabase(String databaseName) throws OlapException { 332 // no op. 333 } 334 335 public String getDatabase() throws OlapException { 336 return getOlapDatabase().getName(); 337 } 338 339 public Database getOlapDatabase() throws OlapException { 340 // It is assumed that Mondrian supports only a single 341 // database. 342 return this.olap4jDatabases.get(0); 343 } 344 345 public NamedList<Database> getOlapDatabases() throws OlapException { 346 return Olap4jUtil.cast(this.olap4jDatabases); 347 } 348 349 public void setTransactionIsolation(int level) throws SQLException { 350 throw new UnsupportedOperationException(); 351 } 352 353 public int getTransactionIsolation() throws SQLException { 354 return TRANSACTION_NONE; 355 } 356 357 public SQLWarning getWarnings() throws SQLException { 358 throw new UnsupportedOperationException(); 359 } 360 361 public void clearWarnings() throws SQLException { 362 } 363 364 public Statement createStatement( 365 int resultSetType, int resultSetConcurrency) throws SQLException 366 { 367 throw new UnsupportedOperationException(); 368 } 369 370 public PreparedStatement prepareStatement( 371 String sql, 372 int resultSetType, 373 int resultSetConcurrency) throws SQLException 374 { 375 throw new UnsupportedOperationException(); 376 } 377 378 public CallableStatement prepareCall( 379 String sql, 380 int resultSetType, 381 int resultSetConcurrency) throws SQLException 382 { 383 throw new UnsupportedOperationException(); 384 } 385 386 public Map<String, Class<?>> getTypeMap() throws SQLException { 387 throw new UnsupportedOperationException(); 388 } 389 390 public void setTypeMap(Map<String, Class<?>> map) throws SQLException { 391 throw new UnsupportedOperationException(); 392 } 393 394 public void setHoldability(int holdability) throws SQLException { 395 throw new UnsupportedOperationException(); 396 } 397 398 public int getHoldability() throws SQLException { 399 throw new UnsupportedOperationException(); 400 } 401 402 public Savepoint setSavepoint() throws SQLException { 403 throw new UnsupportedOperationException(); 404 } 405 406 public Savepoint setSavepoint(String name) throws SQLException { 407 throw new UnsupportedOperationException(); 408 } 409 410 public void rollback(Savepoint savepoint) throws SQLException { 411 throw new UnsupportedOperationException(); 412 } 413 414 public void releaseSavepoint(Savepoint savepoint) throws SQLException { 415 throw new UnsupportedOperationException(); 416 } 417 418 public Statement createStatement( 419 int resultSetType, 420 int resultSetConcurrency, 421 int resultSetHoldability) throws SQLException 422 { 423 throw new UnsupportedOperationException(); 424 } 425 426 public PreparedStatement prepareStatement( 427 String sql, 428 int resultSetType, 429 int resultSetConcurrency, 430 int resultSetHoldability) throws SQLException 431 { 432 throw new UnsupportedOperationException(); 433 } 434 435 public CallableStatement prepareCall( 436 String sql, 437 int resultSetType, 438 int resultSetConcurrency, 439 int resultSetHoldability) throws SQLException 440 { 441 throw new UnsupportedOperationException(); 442 } 443 444 public PreparedStatement prepareStatement( 445 String sql, int autoGeneratedKeys) throws SQLException 446 { 447 throw new UnsupportedOperationException(); 448 } 449 450 public PreparedStatement prepareStatement( 451 String sql, int columnIndexes[]) throws SQLException 452 { 453 throw new UnsupportedOperationException(); 454 } 455 456 public PreparedStatement prepareStatement( 457 String sql, String columnNames[]) throws SQLException 458 { 459 throw new UnsupportedOperationException(); 460 } 461 462 // implement Wrapper 463 464 public <T> T unwrap(Class<T> iface) throws SQLException { 465 if (iface.isInstance(this)) { 466 return iface.cast(this); 467 } else if (iface.isInstance(mondrianConnection)) { 468 return iface.cast(mondrianConnection); 469 } 470 if (iface == XmlaHandler.XmlaExtra.class) { 471 return iface.cast(MondrianOlap4jExtra.INSTANCE); 472 } 473 throw helper.createException("does not implement '" + iface + "'"); 474 } 475 476 public boolean isWrapperFor(Class<?> iface) throws SQLException { 477 return iface.isInstance(this) 478 || iface.isInstance(mondrianConnection); 479 } 480 481 // implement OlapConnection 482 483 public PreparedOlapStatement prepareOlapStatement( 484 String mdx) 485 throws OlapException 486 { 487 final MondrianOlap4jPreparedStatement preparedStatement = 488 factory.newPreparedStatement(mdx, this); 489 mondrianServer.addStatement(preparedStatement); 490 return preparedStatement; 491 } 492 493 public MdxParserFactory getParserFactory() { 494 return new MdxParserFactory() { 495 public MdxParser createMdxParser(OlapConnection connection) { 496 return new DefaultMdxParserImpl(); 497 } 498 499 public MdxValidator createMdxValidator(OlapConnection connection) { 500 return new MondrianOlap4jMdxValidator(connection); 501 } 502 }; 503 } 504 505 MondrianOlap4jCube toOlap4j(mondrian.olap.Cube cube) { 506 MondrianOlap4jSchema schema = toOlap4j(cube.getSchema()); 507 return new MondrianOlap4jCube(cube, schema); 508 } 509 510 MondrianOlap4jDimension toOlap4j(mondrian.olap.Dimension dimension) { 511 if (dimension == null) { 512 return null; 513 } 514 return new MondrianOlap4jDimension( 515 toOlap4j(dimension.getSchema()), 516 dimension); 517 } 518 519 synchronized MondrianOlap4jSchema toOlap4j( 520 mondrian.olap.Schema schema) 521 { 522 MondrianOlap4jSchema olap4jSchema = schemaMap.get(schema); 523 if (olap4jSchema == null) { 524 throw new RuntimeException("schema not registered: " + schema); 525 } 526 return olap4jSchema; 527 } 528 529 Type toOlap4j(mondrian.olap.type.Type type) { 530 if (type instanceof mondrian.olap.type.BooleanType) { 531 return new BooleanType(); 532 } else if (type instanceof mondrian.olap.type.CubeType) { 533 final mondrian.olap.Cube mondrianCube = 534 ((mondrian.olap.type.CubeType) type).getCube(); 535 return new CubeType(toOlap4j(mondrianCube)); 536 } else if (type instanceof mondrian.olap.type.DecimalType) { 537 mondrian.olap.type.DecimalType decimalType = 538 (mondrian.olap.type.DecimalType) type; 539 return new DecimalType( 540 decimalType.getPrecision(), 541 decimalType.getScale()); 542 } else if (type instanceof mondrian.olap.type.DimensionType) { 543 mondrian.olap.type.DimensionType dimensionType = 544 (mondrian.olap.type.DimensionType) type; 545 return new DimensionType( 546 toOlap4j(dimensionType.getDimension())); 547 } else if (type instanceof mondrian.olap.type.HierarchyType) { 548 return new BooleanType(); 549 } else if (type instanceof mondrian.olap.type.LevelType) { 550 return new BooleanType(); 551 } else if (type instanceof mondrian.olap.type.MemberType) { 552 final mondrian.olap.type.MemberType memberType = 553 (mondrian.olap.type.MemberType) type; 554 return new MemberType( 555 toOlap4j(memberType.getDimension()), 556 toOlap4j(memberType.getHierarchy()), 557 toOlap4j(memberType.getLevel()), 558 toOlap4j(memberType.getMember())); 559 } else if (type instanceof mondrian.olap.type.NullType) { 560 return new NullType(); 561 } else if (type instanceof mondrian.olap.type.NumericType) { 562 return new NumericType(); 563 } else if (type instanceof mondrian.olap.type.SetType) { 564 final mondrian.olap.type.SetType setType = 565 (mondrian.olap.type.SetType) type; 566 return new SetType(toOlap4j(setType.getElementType())); 567 } else if (type instanceof mondrian.olap.type.StringType) { 568 return new StringType(); 569 } else if (type instanceof mondrian.olap.type.TupleType) { 570 mondrian.olap.type.TupleType tupleType = 571 (mondrian.olap.type.TupleType) type; 572 final Type[] types = toOlap4j(tupleType.elementTypes); 573 return new TupleType(types); 574 } else if (type instanceof mondrian.olap.type.SymbolType) { 575 return new SymbolType(); 576 } else { 577 throw new UnsupportedOperationException(); 578 } 579 } 580 581 MondrianOlap4jMember toOlap4j(mondrian.olap.Member member) { 582 if (member == null) { 583 return null; 584 } 585 if (member instanceof RolapMeasure) { 586 RolapMeasure measure = (RolapMeasure) member; 587 return new MondrianOlap4jMeasure( 588 toOlap4j(member.getDimension().getSchema()), 589 measure); 590 } 591 return new MondrianOlap4jMember( 592 toOlap4j(member.getDimension().getSchema()), 593 member); 594 } 595 596 MondrianOlap4jLevel toOlap4j(mondrian.olap.Level level) { 597 if (level == null) { 598 return null; 599 } 600 return new MondrianOlap4jLevel( 601 toOlap4j(level.getDimension().getSchema()), 602 level); 603 } 604 605 MondrianOlap4jHierarchy toOlap4j(mondrian.olap.Hierarchy hierarchy) { 606 if (hierarchy == null) { 607 return null; 608 } 609 return new MondrianOlap4jHierarchy( 610 toOlap4j(hierarchy.getDimension().getSchema()), 611 hierarchy); 612 } 613 614 Type[] toOlap4j(mondrian.olap.type.Type[] mondrianTypes) { 615 final Type[] types = new Type[mondrianTypes.length]; 616 for (int i = 0; i < types.length; i++) { 617 types[i] = toOlap4j(mondrianTypes[i]); 618 } 619 return types; 620 } 621 622 NamedList<MondrianOlap4jMember> toOlap4j( 623 final List<Member> memberList) 624 { 625 return new AbstractNamedList<MondrianOlap4jMember>() { 626 public String getName(Object olap4jMember) { 627 return ((MondrianOlap4jMember)olap4jMember).getName(); 628 } 629 630 public MondrianOlap4jMember get(int index) { 631 return toOlap4j(memberList.get(index)); 632 } 633 634 public int size() { 635 return memberList.size(); 636 } 637 }; 638 } 639 640 MondrianOlap4jNamedSet toOlap4j( 641 mondrian.olap.Cube cube, 642 mondrian.olap.NamedSet namedSet) 643 { 644 if (namedSet == null) { 645 return null; 646 } 647 return new MondrianOlap4jNamedSet( 648 toOlap4j(cube), 649 namedSet); 650 } 651 652 ParseTreeNode toOlap4j(Exp exp) { 653 return new MondrianToOlap4jNodeConverter(this).toOlap4j(exp); 654 } 655 656 SelectNode toOlap4j(Query query) { 657 return new MondrianToOlap4jNodeConverter(this).toOlap4j(query); 658 } 659 660 public void setLocale(Locale locale) { 661 mondrianConnection.setLocale(locale); 662 } 663 664 public Locale getLocale() { 665 return mondrianConnection.getLocale(); 666 } 667 668 public void setRoleName(String roleName) throws OlapException { 669 if (roleName == null) { 670 final RolapConnection connection1 = getMondrianConnection(); 671 final Role role = Util.createRootRole(connection1.getSchema()); 672 assert role != null; 673 this.roleName = roleName; 674 this.roleNames = Collections.emptyList(); 675 connection1.setRole(role); 676 } else { 677 setRoleNames(Collections.singletonList(roleName)); 678 } 679 } 680 681 /** 682 * <p>Set the active role(s) in this connection based on a list of role 683 * names.</p> 684 * 685 * <p>The list may be not be empty. Each role name must be not-null and the 686 * name of a valid role for the current user.</p> 687 * 688 * <p>This method is not part of the olap4j-1.x API. It may be included 689 * in olap4j-2.0. If you want to call this method on a 690 * {@link OlapConnection}, use {@link #unwrap} to get the underlying 691 * Mondrian connection.</p> 692 * 693 * @param roleNames List of role names 694 * 695 * @see #getRoleNames() 696 */ 697 public void setRoleNames(List<String> roleNames) throws OlapException { 698 final RolapConnection connection1 = getMondrianConnection(); 699 final List<Role> roleList = new ArrayList<Role>(); 700 for (String roleName : roleNames) { 701 if (roleName == null) { 702 throw new NullPointerException("null role name"); 703 } 704 final Role role = connection1.getSchema().lookupRole(roleName); 705 if (role == null) { 706 throw helper.createException("Unknown role '" + roleName + "'"); 707 } 708 roleList.add(role); 709 } 710 711 // Remember the name of the role, because mondrian roles don't know 712 // their own name. 713 Role role; 714 switch (roleList.size()) { 715 case 0: 716 throw helper.createException("Empty list of role names"); 717 case 1: 718 role = roleList.get(0); 719 this.roleName = roleNames.get(0); 720 this.roleNames = Collections.singletonList(roleName); 721 break; 722 default: 723 role = RoleImpl.union(roleList); 724 this.roleNames = 725 Collections.unmodifiableList(new ArrayList<String>(roleNames)); 726 this.roleName = this.roleNames.toString(); 727 break; 728 } 729 connection1.setRole(role); 730 } 731 732 public String getRoleName() { 733 return roleName; 734 } 735 736 /** 737 * Returns a list of the current role names. 738 * 739 * <p>This method is not part of the olap4j-1.x API. It may be included 740 * in olap4j-2.0. If you want to call this method on a 741 * {@link OlapConnection}, use {@link #unwrap} to get the underlying 742 * Mondrian connection.</p> 743 * 744 * @return List of the current role names 745 */ 746 public List<String> getRoleNames() { 747 return roleNames; 748 } 749 750 public List<String> getAvailableRoleNames() throws OlapException { 751 return UnmodifiableArrayList.of( 752 getMondrianConnection().getSchema().roleNames()); 753 } 754 755 public void setPreferList(boolean preferList) { 756 this.preferList = preferList; 757 } 758 759 /** 760 * Cop-out version of {@link #getMondrianConnection()} that doesn't throw 761 * a checked exception. For those situations where the olap4j API doesn't 762 * declare 'throws OlapException', but we need an open connection anyway. 763 * Use {@link #getMondrianConnection()} where possible. 764 * 765 * @return Mondrian connection 766 * @throws RuntimeException if connection is closed 767 */ 768 RolapConnection getMondrianConnection2() throws RuntimeException { 769 try { 770 return getMondrianConnection(); 771 } catch (OlapException e) { 772 // Demote from checked to unchecked exception. 773 throw new RuntimeException(e); 774 } 775 } 776 777 RolapConnection getMondrianConnection() throws OlapException { 778 final RolapConnection connection1 = mondrianConnection; 779 if (connection1 == null) { 780 throw helper.createException("Connection is closed."); 781 } 782 return connection1; 783 } 784 785 // inner classes 786 787 /** 788 * Package-private helper class which encapsulates policies which are 789 * common throughout the driver. These policies include exception handling 790 * and factory methods. 791 */ 792 static class Helper { 793 OlapException createException(String msg) { 794 return new OlapException(msg); 795 } 796 797 /** 798 * Creates an exception in the context of a particular Cell. 799 * 800 * @param context Cell context for exception 801 * @param msg Message 802 * @return New exception 803 */ 804 OlapException createException(Cell context, String msg) { 805 OlapException exception = new OlapException(msg); 806 exception.setContext(context); 807 return exception; 808 } 809 810 /** 811 * Creates an exception in the context of a particular Cell and with 812 * a given cause. 813 * 814 * @param context Cell context for exception 815 * @param msg Message 816 * @param cause Causing exception 817 * @return New exception 818 */ 819 OlapException createException( 820 Cell context, String msg, Throwable cause) 821 { 822 OlapException exception = createException(msg, cause); 823 exception.setContext(context); 824 return exception; 825 } 826 827 /** 828 * Creates an exception with a given cause. 829 * 830 * @param msg Message 831 * @param cause Causing exception 832 * @return New exception 833 */ 834 OlapException createException( 835 String msg, Throwable cause) 836 { 837 String sqlState = deduceSqlState(cause); 838 assert !mondrian.util.Bug.olap4jUpgrade( 839 "use OlapException(String, String, Throwable) ctor"); 840 final OlapException e = new OlapException(msg, sqlState); 841 e.initCause(cause); 842 return e; 843 } 844 845 private String deduceSqlState(Throwable cause) { 846 if (cause == null) { 847 return null; 848 } 849 if (cause instanceof ResourceLimitExceededException) { 850 return "ResourceLimitExceeded"; 851 } 852 if (cause instanceof QueryTimeoutException) { 853 return "QueryTimeout"; 854 } 855 if (cause instanceof MondrianEvaluationException) { 856 return "EvaluationException"; 857 } 858 if (cause instanceof QueryCanceledException) { 859 return "QueryCanceledException"; 860 } 861 return null; 862 } 863 864 /** 865 * Converts a SQLException to an OlapException. Casts the exception 866 * if it is already an OlapException, wraps otherwise. 867 * 868 * <p>This method is typically used as an adapter for SQLException 869 * instances coming from a base class, where derived interface declares 870 * that it throws the more specific OlapException. 871 * 872 * @param e Exception 873 * @return Exception as an OlapException 874 */ 875 public OlapException toOlapException(SQLException e) { 876 if (e instanceof OlapException) { 877 return (OlapException) e; 878 } else { 879 return new OlapException(null, e); 880 } 881 } 882 } 883 884 private static class MondrianOlap4jMdxValidator implements MdxValidator { 885 private final MondrianOlap4jConnection connection; 886 887 public MondrianOlap4jMdxValidator(OlapConnection connection) { 888 this.connection = (MondrianOlap4jConnection) connection; 889 } 890 891 public SelectNode validateSelect(SelectNode selectNode) 892 throws OlapException 893 { 894 try { 895 // A lot of mondrian's validation happens during parsing. 896 // Therefore to do effective validation, we need to go back to 897 // the MDX string. Someday we will reshape mondrian's 898 // parse/validation process to fit the olap4j model better. 899 StringWriter sw = new StringWriter(); 900 selectNode.unparse(new ParseTreeWriter(new PrintWriter(sw))); 901 String mdx = sw.toString(); 902 Query query = 903 connection.mondrianConnection 904 .parseQuery(mdx); 905 query.resolve(); 906 return connection.toOlap4j(query); 907 } catch (MondrianException e) { 908 throw connection.helper.createException("Validation error", e); 909 } 910 } 911 } 912 913 private static class MondrianToOlap4jNodeConverter { 914 private final MondrianOlap4jConnection olap4jConnection; 915 916 MondrianToOlap4jNodeConverter( 917 MondrianOlap4jConnection olap4jConnection) 918 { 919 this.olap4jConnection = olap4jConnection; 920 } 921 922 public SelectNode toOlap4j(Query query) { 923 List<IdentifierNode> list = Collections.emptyList(); 924 return new SelectNode( 925 null, 926 toOlap4j(query.getFormulas()), 927 toOlap4j(query.getAxes()), 928 new CubeNode( 929 null, 930 olap4jConnection.toOlap4j(query.getCube())), 931 query.getSlicerAxis() == null 932 ? null 933 : toOlap4j(query.getSlicerAxis()), 934 list); 935 } 936 937 private AxisNode toOlap4j(QueryAxis axis) { 938 return new AxisNode( 939 null, 940 axis.isNonEmpty(), 941 Axis.Factory.forOrdinal( 942 axis.getAxisOrdinal().logicalOrdinal()), 943 toOlap4j(axis.getDimensionProperties()), 944 toOlap4j(axis.getSet())); 945 } 946 947 private List<IdentifierNode> toOlap4j(Id[] dimensionProperties) { 948 final List<IdentifierNode> list = new ArrayList<IdentifierNode>(); 949 for (Id property : dimensionProperties) { 950 list.add(toOlap4j(property)); 951 } 952 return list; 953 } 954 955 private ParseTreeNode toOlap4j(Exp exp) { 956 if (exp instanceof Id) { 957 Id id = (Id) exp; 958 return toOlap4j(id); 959 } 960 if (exp instanceof ResolvedFunCall) { 961 ResolvedFunCall call = (ResolvedFunCall) exp; 962 return toOlap4j(call); 963 } 964 if (exp instanceof DimensionExpr) { 965 DimensionExpr dimensionExpr = (DimensionExpr) exp; 966 return new DimensionNode( 967 null, 968 olap4jConnection.toOlap4j(dimensionExpr.getDimension())); 969 } 970 if (exp instanceof HierarchyExpr) { 971 HierarchyExpr hierarchyExpr = (HierarchyExpr) exp; 972 return new HierarchyNode( 973 null, 974 olap4jConnection.toOlap4j(hierarchyExpr.getHierarchy())); 975 } 976 if (exp instanceof LevelExpr) { 977 LevelExpr levelExpr = (LevelExpr) exp; 978 return new LevelNode( 979 null, 980 olap4jConnection.toOlap4j(levelExpr.getLevel())); 981 } 982 if (exp instanceof MemberExpr) { 983 MemberExpr memberExpr = (MemberExpr) exp; 984 return new MemberNode( 985 null, 986 olap4jConnection.toOlap4j(memberExpr.getMember())); 987 } 988 if (exp instanceof Literal) { 989 Literal literal = (Literal) exp; 990 final Object value = literal.getValue(); 991 if (literal.getCategory() == Category.Symbol) { 992 return LiteralNode.createSymbol( 993 null, (String) literal.getValue()); 994 } else if (value instanceof Number) { 995 Number number = (Number) value; 996 BigDecimal bd = bigDecimalFor(number); 997 return LiteralNode.createNumeric(null, bd, false); 998 } else if (value instanceof String) { 999 return LiteralNode.createString(null, (String) value); 1000 } else if (value == null) { 1001 return LiteralNode.createNull(null); 1002 } else { 1003 throw new RuntimeException("unknown literal " + literal); 1004 } 1005 } 1006 throw Util.needToImplement(exp.getClass()); 1007 } 1008 1009 /** 1010 * Converts a number to big decimal, non-lossy if possible. 1011 * 1012 * @param number Number 1013 * @return BigDecimal 1014 */ 1015 private static BigDecimal bigDecimalFor(Number number) { 1016 if (number instanceof BigDecimal) { 1017 return (BigDecimal) number; 1018 } else if (number instanceof BigInteger) { 1019 return new BigDecimal((BigInteger) number); 1020 } else if (number instanceof Integer) { 1021 return new BigDecimal((Integer) number); 1022 } else if (number instanceof Double) { 1023 return new BigDecimal((Double) number); 1024 } else if (number instanceof Float) { 1025 return new BigDecimal((Float) number); 1026 } else if (number instanceof Long) { 1027 return new BigDecimal((Long) number); 1028 } else if (number instanceof Short) { 1029 return new BigDecimal((Short) number); 1030 } else if (number instanceof Byte) { 1031 return new BigDecimal((Byte) number); 1032 } else { 1033 return new BigDecimal(number.doubleValue()); 1034 } 1035 } 1036 1037 private ParseTreeNode toOlap4j(ResolvedFunCall call) { 1038 final CallNode callNode = new CallNode( 1039 null, 1040 call.getFunName(), 1041 toOlap4j(call.getSyntax()), 1042 toOlap4j(Arrays.asList(call.getArgs()))); 1043 if (call.getType() != null) { 1044 callNode.setType(olap4jConnection.toOlap4j(call.getType())); 1045 } 1046 return callNode; 1047 } 1048 1049 private List<ParseTreeNode> toOlap4j(List<Exp> exprList) { 1050 final List<ParseTreeNode> result = new ArrayList<ParseTreeNode>(); 1051 for (Exp expr : exprList) { 1052 result.add(toOlap4j(expr)); 1053 } 1054 return result; 1055 } 1056 1057 private org.olap4j.mdx.Syntax toOlap4j(mondrian.olap.Syntax syntax) { 1058 return org.olap4j.mdx.Syntax.valueOf(syntax.name()); 1059 } 1060 1061 private List<AxisNode> toOlap4j(QueryAxis[] axes) { 1062 final ArrayList<AxisNode> axisList = new ArrayList<AxisNode>(); 1063 for (QueryAxis axis : axes) { 1064 axisList.add(toOlap4j(axis)); 1065 } 1066 return axisList; 1067 } 1068 1069 private List<ParseTreeNode> toOlap4j(Formula[] formulas) { 1070 final List<ParseTreeNode> list = new ArrayList<ParseTreeNode>(); 1071 for (Formula formula : formulas) { 1072 if (formula.isMember()) { 1073 List<PropertyValueNode> memberPropertyList = 1074 new ArrayList<PropertyValueNode>(); 1075 for (Object child : formula.getChildren()) { 1076 if (child instanceof MemberProperty) { 1077 MemberProperty memberProperty = 1078 (MemberProperty) child; 1079 memberPropertyList.add( 1080 new PropertyValueNode( 1081 null, 1082 memberProperty.getName(), 1083 toOlap4j(memberProperty.getExp()))); 1084 } 1085 } 1086 list.add( 1087 new WithMemberNode( 1088 null, 1089 toOlap4j(formula.getIdentifier()), 1090 toOlap4j(formula.getExpression()), 1091 memberPropertyList)); 1092 } 1093 } 1094 return list; 1095 } 1096 1097 private static IdentifierNode toOlap4j(Id id) { 1098 List<IdentifierSegment> list = Util.toOlap4j(id.getSegments()); 1099 return new IdentifierNode( 1100 list.toArray( 1101 new IdentifierSegment[list.size()])); 1102 } 1103 } 1104} 1105 1106// End MondrianOlap4jConnection.java