001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/MondrianProperties.java#100 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2001-2002 Kana Software, Inc. 007 // Copyright (C) 2001-2008 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 // 011 // jhyde, 22 December, 2002 012 */ 013 package mondrian.olap; 014 015 import org.apache.log4j.Logger; 016 import org.eigenbase.util.property.*; 017 018 import java.io.*; 019 import java.net.MalformedURLException; 020 import java.net.URL; 021 import java.net.URLConnection; 022 import java.util.Enumeration; 023 import java.util.Properties; 024 025 /** 026 * <code>MondrianProperties</code> contains the properties which determine the 027 * behavior of a mondrian instance. 028 * 029 * <p>There is a method for property valid in a 030 * <code>mondrian.properties</code> file. Although it is possible to retrieve 031 * properties using the inherited {@link Properties#getProperty(String)} 032 * method, we recommend that you use methods in this class. 033 * 034 * <h2>Note to developers</h2> 035 * 036 * If you add a property, you must:<ul> 037 * 038 * <li>Add a property definition to this class</li> 039 * 040 * <li>Modify the default <code>mondrian.properties</code> file checked into 041 * source control, with a description of the property and its default 042 * value.</li> 043 * 044 * <li>Modify the 045 * <a target="_top" href="{@docRoot}/../configuration.html#Property_list"> 046 * Configuration Specification</a>.</li> 047 * </ul> 048 * 049 * <p>Similarly if you update or delete a property. 050 * 051 * @author jhyde 052 * @version $Id: //open/mondrian/src/main/mondrian/olap/MondrianProperties.java#100 $ 053 * @since 22 December, 2002 054 */ 055 public class MondrianProperties extends TriggerableProperties { 056 057 private final PropertySource propertySource; 058 private int populateCount; 059 060 private static final Logger LOGGER = 061 Logger.getLogger(MondrianProperties.class); 062 063 /** 064 * Properties, drawn from {@link System#getProperties}, plus the contents 065 * of "mondrian.properties" if it exists. A singleton. 066 */ 067 private static MondrianProperties instance; 068 private static final String mondrianDotProperties = "mondrian.properties"; 069 070 /** 071 * Returns the singleton. 072 * 073 * @return Singleton instance 074 */ 075 public static synchronized MondrianProperties instance() { 076 if (instance == null) { 077 instance = new MondrianProperties(); 078 instance.populate(); 079 } 080 return instance; 081 } 082 083 public MondrianProperties() { 084 this.propertySource = new FilePropertySource(new File(mondrianDotProperties)); 085 } 086 087 public boolean triggersAreEnabled() { 088 return EnableTriggers.get(); 089 } 090 091 /** 092 * Represents a place that properties can be read from, and remembers the 093 * timestamp that we last read them. 094 */ 095 public interface PropertySource { 096 /** 097 * Opens an input stream from the source. 098 * 099 * <p>Also checks the 'last modified' time, which will determine whether 100 * {@link #isStale()} returns true. 101 * 102 * @return input stream 103 */ 104 InputStream openStream(); 105 106 /** 107 * Returns true if the source exists and has been modified since last 108 * time we called {@link #openStream()}. 109 * 110 * @return whether source has changed since it was last read 111 */ 112 boolean isStale(); 113 114 /** 115 * Returns the description of this source, such as a filename or URL. 116 * 117 * @return description of this PropertySource 118 */ 119 String getDescription(); 120 } 121 122 /** 123 * Implementation of {@link PropertySource} which reads from a 124 * {@link File}. 125 */ 126 static class FilePropertySource implements PropertySource { 127 private final File file; 128 private long lastModified; 129 130 FilePropertySource(File file) { 131 this.file = file; 132 this.lastModified = 0; 133 } 134 135 public InputStream openStream() { 136 try { 137 this.lastModified = file.lastModified(); 138 return new FileInputStream(file); 139 } catch (FileNotFoundException e) { 140 throw Util.newInternal( 141 e, 142 "Error while opening properties file '" + file + "'"); 143 } 144 } 145 146 public boolean isStale() { 147 return file.exists() && 148 file.lastModified() > this.lastModified; 149 } 150 151 public String getDescription() { 152 return "file=" + file.getAbsolutePath() + 153 " (exists=" + file.exists() + ")"; 154 } 155 } 156 157 /** 158 * Implementation of {@link PropertySource} which reads from a {@link URL}. 159 */ 160 static class UrlPropertySource implements PropertySource { 161 private final URL url; 162 private long lastModified; 163 164 UrlPropertySource(URL url) { 165 this.url = url; 166 } 167 168 private URLConnection getConnection() { 169 try { 170 return url.openConnection(); 171 } catch (IOException e) { 172 throw Util.newInternal( 173 e, 174 "Error while opening properties file '" + url + "'"); 175 } 176 } 177 178 public InputStream openStream() { 179 try { 180 final URLConnection connection = getConnection(); 181 this.lastModified = connection.getLastModified(); 182 return connection.getInputStream(); 183 } catch (IOException e) { 184 throw Util.newInternal( 185 e, 186 "Error while opening properties file '" + url + "'"); 187 } 188 } 189 190 public boolean isStale() { 191 final long lastModified = getConnection().getLastModified(); 192 return lastModified > this.lastModified; 193 } 194 195 public String getDescription() { 196 return url.toExternalForm(); 197 } 198 } 199 200 /** 201 * Loads this property set from: the file "$PWD/mondrian.properties" (if it 202 * exists); the "mondrian.properties" in the CLASSPATH; and from the system 203 * properties. 204 */ 205 public void populate() { 206 // Read properties file "mondrian.properties", if it exists. If we have 207 // read the file before, only read it if it is newer. 208 loadIfStale(propertySource); 209 210 URL url = null; 211 File file = new File(mondrianDotProperties); 212 if (file.exists() && file.isFile()) { 213 // Read properties file "mondrian.properties" from PWD, if it 214 // exists. 215 try { 216 url = file.toURI().toURL(); 217 } catch (MalformedURLException e) { 218 LOGGER.warn("Mondrian: file '" 219 + file.getAbsolutePath() 220 + "' could not be loaded", e); 221 } 222 } else { 223 // Then try load it from classloader 224 url = 225 MondrianProperties.class.getClassLoader().getResource( 226 mondrianDotProperties); 227 } 228 229 if (url != null) { 230 load(new UrlPropertySource(url)); 231 } else { 232 LOGGER.warn("mondrian.properties can't be found under '" 233 + new File(".").getAbsolutePath() + "' or classloader"); 234 } 235 236 // copy in all system properties which start with "mondrian." 237 int count = 0; 238 for (Enumeration keys = System.getProperties().keys(); 239 keys.hasMoreElements();) { 240 String key = (String) keys.nextElement(); 241 String value = System.getProperty(key); 242 if (key.startsWith("mondrian.")) { 243 // NOTE: the super allows us to bybase calling triggers 244 // Is this the correct behavior? 245 if (LOGGER.isDebugEnabled()) { 246 LOGGER.debug("populate: key=" + key + ", value=" + value); 247 } 248 super.setProperty(key, value); 249 count++; 250 } 251 } 252 if (populateCount++ == 0) { 253 LOGGER.info("Mondrian: loaded " 254 + count 255 + " system properties"); 256 } 257 } 258 259 /** 260 * Reads properties from a source. 261 * If the source does not exist, or has not changed since we last read it, 262 * does nothing. 263 * 264 * @param source Source of properties 265 */ 266 private void loadIfStale(PropertySource source) { 267 if (source.isStale()) { 268 if (LOGGER.isDebugEnabled()) { 269 LOGGER.debug("Mondrian: loading " + source.getDescription()); 270 } 271 load(source); 272 } 273 } 274 275 /** 276 * Tries to load properties from a URL. Does not fail, just prints success 277 * or failure to log. 278 * 279 * @param source Source to read properties from 280 */ 281 private void load(final PropertySource source) { 282 try { 283 load(source.openStream()); 284 if (populateCount == 0) { 285 LOGGER.info("Mondrian: properties loaded from '" 286 + source.getDescription() 287 + "'"); 288 } 289 } catch (IOException e) { 290 LOGGER.error("Mondrian: error while loading properties " 291 + "from '" 292 + source.getDescription() 293 + "' (" 294 + e 295 + ")"); 296 } 297 } 298 299 /** 300 * Maximum number of simultaneous queries the system will allow. 301 * 302 * <p>Oracle fails if you try to run more than the 'processes' parameter in 303 * init.ora, typically 150. The throughput of Oracle and other databases 304 * will probably reduce long before you get to their limit.</p> 305 */ 306 public transient final IntegerProperty QueryLimit = new IntegerProperty( 307 this, "mondrian.query.limit", 40); 308 309 /** 310 * Property containing a list of JDBC drivers to load automatically. 311 * Must be a comma-separated list of class names, and the classes must be 312 * on the class path. 313 */ 314 public transient final StringProperty JdbcDrivers = 315 new StringProperty( 316 this, 317 "mondrian.jdbcDrivers", 318 "sun.jdbc.odbc.JdbcOdbcDriver," + 319 "org.hsqldb.jdbcDriver," + 320 "oracle.jdbc.OracleDriver," + 321 "com.mysql.jdbc.Driver"); 322 323 /** 324 * Integer property that, if set to a value greater than zero, limits the 325 * maximum size of a result set. 326 */ 327 public transient final IntegerProperty ResultLimit = 328 new IntegerProperty( 329 this, "mondrian.result.limit", 0); 330 331 /** 332 * Property that establishes the amount of chunks for querying cells 333 * involving high-cardinality dimensions. 334 * Should prime with {@link #ResultLimit mondrian.result.limit}. 335 */ 336 public transient final IntegerProperty HighCardChunkSize = 337 new IntegerProperty(this, "mondrian.result.highCardChunkSize", 1); 338 339 340 // mondrian.test properties 341 342 /** 343 * String property that determines which tests are run. 344 * 345 * <p>This is a regular expression as defined by 346 * {@link java.util.regex.Pattern}. 347 * If this property is specified, only tests whose names match the pattern 348 * in its entirety will be run.</p> 349 * 350 * @see #TestClass 351 */ 352 public transient final StringProperty TestName = 353 new StringProperty( 354 this, "mondrian.test.Name", null); 355 356 /** 357 * String property that determines which test class to run. 358 * 359 * <p>This is the name of the class which either implements 360 * {@code junit.framework.Test} or has a method 361 * {@code public [static] junit.framework.Test suite()}.</p> 362 * 363 * <p>Example: 364 * <blockquote><code>mondrian.test.Class=mondrian.test.FoodMartTestCase</code></blockquote> 365 * 366 * @see #TestName 367 */ 368 public transient final StringProperty TestClass = 369 new StringProperty( 370 this, "mondrian.test.Class", null); 371 372 /** 373 * Property containing the connect string which regresssion tests should 374 * use to connect to the database. 375 * Format is specified in {@link Util#parseConnectString(String)}. 376 */ 377 public transient final StringProperty TestConnectString = 378 new StringProperty( 379 this, "mondrian.test.connectString", null); 380 /** 381 * Property containing a list of dimensions in the Sales cube that should 382 * be treated as high-cardinality dimensions by the testing infrastructure. 383 * This allows us to run the full suite of tests with high-cardinality 384 * functionality enabled. 385 */ 386 public transient final StringProperty TestHighCardinalityDimensionList = 387 new StringProperty( 388 this, "mondrian.test.highCardDimensions", null); 389 390 // miscellaneous 391 392 /** 393 * Property containing the JDBC URL of the FoodMart database. 394 * The default value is to connect to an ODBC data source called 395 * "MondrianFoodMart". 396 */ 397 public transient final StringProperty FoodmartJdbcURL = new StringProperty( 398 this, "mondrian.foodmart.jdbcURL", "jdbc:odbc:MondrianFoodMart"); 399 400 /** 401 * Property containing the JDBC URL of a test database. 402 * It does not default. 403 */ 404 public transient final StringProperty TestJdbcURL = new StringProperty( 405 this, "mondrian.test.jdbcURL", null); 406 407 /** 408 * Property containing the JDBC user of a test database. 409 * The default value is null, to cope with DBMSs that don't need this. 410 */ 411 public transient final StringProperty TestJdbcUser = new StringProperty( 412 this, "mondrian.test.jdbcUser", null); 413 414 /** 415 * Property containing the JDBC password of a test database. 416 * The default value is null, to cope with DBMSs that don't need this. 417 */ 418 public transient final StringProperty TestJdbcPassword = new StringProperty( 419 this, "mondrian.test.jdbcPassword", null); 420 421 /** 422 * Property that, with {@link #SparseSegmentDensityThreshold}, determines 423 * whether to choose a sparse or dense representation when storing 424 * collections of cell values in memory. 425 * 426 * <p>When storing collections of cell values, Mondrian has to choose 427 * between a sparse and a dense representation, based upon the 428 * <code>possible</code> and <code>actual</code> number of values. 429 * The <code>density</code> is <code>actual / possible</code>. 430 * 431 * <p>We use a sparse representation if 432 * <code>(possible - 433 * {@link #SparseSegmentCountThreshold countThreshold}) * 434 * {@link #SparseSegmentDensityThreshold densityThreshold} > 435 * actual</code> 436 * 437 * <p>For example, at the default values 438 * ({@link #SparseSegmentCountThreshold countThreshold} = 1000, 439 * {@link #SparseSegmentDensityThreshold} = 0.5), 440 * we use a dense representation for<ul> 441 * <li>(1000 possible, 0 actual), or 442 * <li>(2000 possible, 500 actual), or 443 * <li>(3000 possible, 1000 actual). 444 * </ul> 445 * Any fewer actual values, or any more 446 * possible values, and Mondrian will use a sparse representation. 447 */ 448 public transient final IntegerProperty SparseSegmentCountThreshold = 449 new IntegerProperty( 450 this, "mondrian.rolap.SparseSegmentValueThreshold", 1000); 451 452 /** 453 * Property that, with {@link #SparseSegmentCountThreshold}, 454 * determines whether to choose a sparse or dense representation when 455 * storing collections of cell values in memory. 456 */ 457 public transient final DoubleProperty SparseSegmentDensityThreshold = 458 new DoubleProperty( 459 this, "mondrian.rolap.SparseSegmentDensityThreshold", 0.5); 460 461 /** 462 * Property that defines 463 * a pattern for which test XML files to run. Pattern has to 464 * match a file name of the form: 465 * <code>query<i>whatever</i>.xml</code> in the directory. 466 * 467 * <p>Example: 468 * <blockquote><code>mondrian.test.QueryFilePattern=queryTest_fec[A-Za-z0-9_]*.xml</code></blockquote> 469 * 470 */ 471 public transient final StringProperty QueryFilePattern = 472 new StringProperty( 473 this, "mondrian.test.QueryFilePattern", null); 474 475 /** 476 * Property defining 477 * where the test XML files are. 478 */ 479 public transient final StringProperty QueryFileDirectory = 480 new StringProperty( 481 this, "mondrian.test.QueryFileDirectory", null); 482 483 /** 484 * todo: 485 */ 486 public transient final IntegerProperty Iterations = new IntegerProperty( 487 this, "mondrian.test.Iterations", 1); 488 489 /** 490 * todo: 491 */ 492 public transient final IntegerProperty VUsers = new IntegerProperty( 493 this, "mondrian.test.VUsers", 1); 494 495 /** 496 * Property that returns the time limit for the test run in seconds. 497 * If the test is running after that time, it is terminated. 498 */ 499 public transient final IntegerProperty TimeLimit = new IntegerProperty( 500 this, "mondrian.test.TimeLimit", 0); 501 502 /** 503 * Property that indicates whether this is a "warmup test". 504 */ 505 public transient final BooleanProperty Warmup = new BooleanProperty( 506 this, "mondrian.test.Warmup", false); 507 508 /** 509 * Property that contains the URL of the catalog to be used by 510 * {@link mondrian.tui.CmdRunner} and XML/A Test. 511 */ 512 public transient final StringProperty CatalogURL = new StringProperty( 513 this, "mondrian.catalogURL", null); 514 515 /** 516 * Property that controls 517 * whether aggregation cache hit / miss counters will be enabled 518 */ 519 public transient final BooleanProperty EnableCacheHitCounters = 520 new BooleanProperty( 521 this, "mondrian.rolap.agg.enableCacheHitCounters", false); 522 523 /** 524 * Property that controls if warning messages should be printed if a sql 525 * comparison tests do not contain expected sqls for the specified 526 * dialect. The tests are skipped if no expected sqls are 527 * found for the current dialect. 528 * 529 * Possible values are the following: 530 * "NONE": no warning (default) 531 * "ANY": any dialect 532 * "ACCESS" 533 * "DERBY" 534 * "LUCIDDB" 535 * "MYSQL" 536 * ...and any Dialect enum in SqlPattern.Dialect 537 * 538 * Specific tests can overwrite the default setting. The priority is 539 * Settings besides "ANY" in mondrian.properties file < Any setting in the test < "ANY" 540 * 541 */ 542 public transient final StringProperty WarnIfNoPatternForDialect = 543 new StringProperty( 544 this, "mondrian.test.WarnIfNoPatternForDialect", "NONE"); 545 546 ////////////////////////////////////////////////////////////////////////// 547 // 548 // properties relating to aggregates 549 // 550 551 /** 552 * Boolean property that controls whether Mondrian uses aggregate tables. 553 * 554 * <p>If true, then Mondrian uses aggregate tables. This property is 555 * queried prior to each aggregate query so that changing the value of this 556 * property dynamically (not just at startup) is meaningful. 557 * 558 * <p>Aggregates can be read from the database using the 559 * {@link #ReadAggregates} property but will not be used unless this 560 * property is set to true. 561 */ 562 public transient final BooleanProperty UseAggregates = 563 new BooleanProperty( 564 this, "mondrian.rolap.aggregates.Use", false); 565 566 /** 567 * Boolean property that determines whether Mondrian should read aggregate 568 * tables. 569 * 570 * <p>If set to true, then Mondrian scans the database for aggregate tables. 571 * Unless mondrian.rolap.aggregates.Use is set to true, the aggregates 572 * found will not be used. 573 */ 574 public transient final BooleanProperty ReadAggregates = 575 new BooleanProperty( 576 this, "mondrian.rolap.aggregates.Read", false); 577 578 579 /** 580 * Boolean property that controls whether aggregate tables 581 * are ordered by their volume or row count. 582 * 583 * <p>If true, Mondrian uses the aggregate table with the smallest volume 584 * (number of rows multiplied by number of columns); if false, Mondrian 585 * uses the aggregate table with the fewest rows. 586 */ 587 public transient final BooleanProperty ChooseAggregateByVolume = 588 new BooleanProperty( 589 this, "mondrian.rolap.aggregates.ChooseByVolume", false); 590 591 /** 592 * String property containing the name of the file which defines the rules 593 * for recognizing an aggregate table. Can be either a resource in the 594 * Mondrian jar or a URL. 595 * 596 * <p>The default value is "/DefaultRules.xml", which is in the 597 * mondrian.rolap.aggmatcher package in Mondrian.jar. 598 * 599 * <p>Normally, this property is not set by a user. 600 */ 601 public transient final StringProperty AggregateRules = 602 new StringProperty( 603 this, "mondrian.rolap.aggregates.rules", "/DefaultRules.xml"); 604 605 /** 606 * String property that is the AggRule element's tag value. 607 * 608 * <p>Normally, this property is not set by a user. 609 */ 610 public transient final StringProperty AggregateRuleTag = 611 new StringProperty( 612 this, "mondrian.rolap.aggregates.rule.tag", "default"); 613 614 /** 615 * Boolean property that controls whether to print the SQL code 616 * generated for aggregate tables. 617 * 618 * <p>If set, then as each aggregate request is processed, both the lost 619 * and collapsed dimension create and insert sql code is printed. 620 * This is for use in the CmdRunner allowing one to create aggregate table 621 * generation sql. 622 */ 623 public transient final BooleanProperty GenerateAggregateSql = 624 new BooleanProperty( 625 this, "mondrian.rolap.aggregates.generateSql", false); 626 627 // 628 ////////////////////////////////////////////////////////////////////////// 629 630 /** 631 * Boolean property that controls whether a RolapStar's 632 * aggregate data cache is cleared after each query. 633 * If true, no RolapStar will cache aggregate data from one 634 * query to the next (the cache is cleared after each query). 635 */ 636 public transient final BooleanProperty DisableCaching = 637 new BooleanProperty( 638 this, "mondrian.rolap.star.disableCaching", false); 639 640 /** 641 * Boolean property that controls whether to notify the Mondrian system 642 * when a {@link MondrianProperties property value} changes. 643 * 644 * <p>This allows objects dependent on Mondrian properties to react (that 645 * is, reload), when a given property changes via, say, 646 * <code>MondrianProperties.instance().populate(null)</code> or 647 * <code>MondrianProperties.instance().QueryLimit.set(50)</code>. 648 */ 649 public transient final BooleanProperty EnableTriggers = 650 new BooleanProperty( 651 this, "mondrian.olap.triggers.enable", true); 652 653 /** 654 * Boolean property that controls pretty-print mode. 655 * If set to true, the all SqlQuery SQL strings 656 * will be generated in pretty-print mode, formatted for ease of reading. 657 */ 658 public transient final BooleanProperty GenerateFormattedSql = 659 new BooleanProperty( 660 this, "mondrian.rolap.generate.formatted.sql", false); 661 662 /** 663 * Boolean property that controls whether each query axis implicit has the 664 * NON EMPTY option set. The default is false. 665 */ 666 public transient final BooleanProperty EnableNonEmptyOnAllAxis = 667 new BooleanProperty( 668 this, "mondrian.rolap.nonempty", false); 669 670 /** 671 * When looking for native evaluation of an expression, expand non native 672 * subexpressions into MemberLists. 673 */ 674 public transient final BooleanProperty ExpandNonNative = 675 new BooleanProperty( 676 this, "mondrian.native.ExpandNonNative", false); 677 678 /** 679 * Boolean property that controls whether sibling members are 680 * compared according to order key value fetched from their ordinal 681 * expression. The default is false (only database ORDER BY is used). 682 */ 683 public transient final BooleanProperty CompareSiblingsByOrderKey = 684 new BooleanProperty( 685 this, "mondrian.rolap.compareSiblingsByOrderKey", false); 686 687 /** 688 * Boolean property that controls whether to use a cache for frequently 689 * evaluated expressions. With the cache disabled, an expression like 690 * <code>Rank([Product].CurrentMember, 691 * Order([Product].MEMBERS, [Measures].[Unit Sales]))</code> would perform 692 * many redundant sorts. The default is true. 693 */ 694 public transient final BooleanProperty EnableExpCache = 695 new BooleanProperty( 696 this, "mondrian.expCache.enable", true); 697 698 /** 699 * Integer property that controls whether to test operators' dependencies, 700 * and how much time to spend doing it. 701 * 702 * <p>If this property is positive, Mondrian's test framework allocates an 703 * expression evaluator which evaluates each expression several times, and 704 * makes sure that the results of the expression are independent of 705 * dimensions which the expression claims to be independent of. 706 * 707 * <p>The default is 0. 708 */ 709 public transient final IntegerProperty TestExpDependencies = 710 new IntegerProperty( 711 this, "mondrian.test.ExpDependencies", 0); 712 713 /** 714 * Seed for random number generator used by some of the tests. 715 * 716 * 717 * Any value besides 0 or -1 gives deterministic behavior. 718 * The default value is 1234: most users should use this. 719 * Setting the seed to a different value can increase coverage, and 720 * therefore may uncover new bugs. 721 * 722 * <p>If you set the value to 0, the system will generate its own 723 * pseudo-random seed. 724 * 725 * <p>If you set the value to -1, Mondrian uses the next seed from an 726 * internal random-number generator. This is a little more deterministic 727 * than setting the value to 0. 728 */ 729 public transient final IntegerProperty TestSeed = 730 new IntegerProperty( 731 this, "mondrian.test.random.seed", 1234); 732 733 /** 734 * Name of locale property file. 735 * 736 * <p>Used for the {@link mondrian.i18n.LocalizingDynamicSchemaProcessor}; 737 * see <a href="{@docRoot}/../schema.html#I18n">Internationalization</a> 738 * for more details.</td> 739 * 740 * <p>Default value is null. 741 */ 742 public transient final StringProperty LocalePropFile = 743 new StringProperty( 744 this, "mondrian.rolap.localePropFile", null); 745 746 /** 747 * if enabled some NON EMPTY CrossJoin will be computed in SQL 748 */ 749 public transient final BooleanProperty EnableNativeCrossJoin = 750 new BooleanProperty( 751 this, "mondrian.native.crossjoin.enable", true); 752 753 /** 754 * if enabled some TopCount will be computed in SQL 755 */ 756 public transient final BooleanProperty EnableNativeTopCount = 757 new BooleanProperty( 758 this, "mondrian.native.topcount.enable", true); 759 760 /** 761 * if enabled some Filter() will be computed in SQL 762 */ 763 public transient final BooleanProperty EnableNativeFilter = 764 new BooleanProperty( 765 this, "mondrian.native.filter.enable", true); 766 767 /** 768 * some NON EMPTY set operations like member.children, level.members and 769 * member descendants will be computed in SQL 770 */ 771 public transient final BooleanProperty EnableNativeNonEmpty = 772 new BooleanProperty( 773 this, "mondrian.native.nonempty.enable", true); 774 775 /** 776 * Alerting action to take in case native evaluation of a function is 777 * enabled but not supported for that function's usage in a particular 778 * query. (No alert is ever raised in cases where native evaluation would 779 * definitely have been wasted effort.) 780 * 781 * 782 * 783 * Recognized actions: 784 * 785 * <ul> 786 * 787 * <li><code>OFF</code>: do nothing (default action, also used if 788 * unrecognized action is specified) 789 * 790 * <li><code>WARN</code>: log a warning to RolapUtil logger 791 * 792 * <li><code>ERROR</code>: throw an instance of 793 * {@link NativeEvaluationUnsupportedException} 794 * 795 * </ul> 796 */ 797 public transient final StringProperty AlertNativeEvaluationUnsupported = 798 new StringProperty(this, "mondrian.native.unsupported.alert", "OFF"); 799 800 /** 801 * If enabled, first row in the result of an XML/A drill-through request 802 * will be filled with the total count of rows in underlying database. 803 */ 804 public transient final BooleanProperty EnableTotalCount = 805 new BooleanProperty( 806 this, "mondrian.xmla.drillthroughTotalCount.enable", true); 807 808 /** 809 * Boolean property that controls whether the MDX parser resolves uses 810 * case-sensitive matching when looking up identifiers. The default is 811 * false. 812 */ 813 public transient final BooleanProperty CaseSensitive = new BooleanProperty( 814 this, "mondrian.olap.case.sensitive", false); 815 816 817 /** 818 * Property that defines 819 * limit on the number of rows returned by XML/A drill through request. 820 */ 821 public transient final IntegerProperty MaxRows = new IntegerProperty( 822 this, "mondrian.xmla.drillthroughMaxRows", 1000); 823 824 /** 825 * Max number of constraints in a single `IN' SQL clause. 826 * 827 * <p>This value may be variant among database prodcuts and their runtime 828 * settings. Oracle, for example, gives the error "ORA-01795: maximum 829 * number of expressions in a list is 1000". 830 * 831 * <p>Recommended values:<ul> 832 * <li>Oracle: 1,000 833 * <li>DB2: 2,500 834 * <li>Other: 10,000</ul> 835 */ 836 public transient final IntegerProperty MaxConstraints = new IntegerProperty( 837 this, "mondrian.rolap.maxConstraints", 1000); 838 839 /** 840 * Boolean property that determines whether Mondrian optimizes predicates. 841 */ 842 public transient final BooleanProperty OptimizePredicates = 843 new BooleanProperty(this, 844 "mondrian.rolap.aggregates.optimizePredicates", 845 true); 846 847 /** 848 * Boolean property that defines the 849 * maximum number of passes allowable while evaluating an MDX expression. 850 * 851 * <p>If evaluation exceeds this depth (for example, while evaluating a 852 * very complex calculated member), Mondrian will throw an error. 853 */ 854 public transient final IntegerProperty MaxEvalDepth = 855 new IntegerProperty( 856 this, "mondrian.rolap.evaluate.MaxEvalDepth", 10); 857 858 /** 859 * Property that defines the JdbcSchema factory class which 860 * determines the list of tables and columns of a specific datasource. 861 * @see mondrian.rolap.aggmatcher.JdbcSchema 862 */ 863 public transient final StringProperty JdbcFactoryClass = 864 new StringProperty( 865 this, "mondrian.rolap.aggregates.jdbcFactoryClass", null); 866 867 /** 868 * Property that defines 869 * the timeout value (in seconds) for queries; 0, the default, indicates no 870 * timeout. 871 */ 872 public transient final IntegerProperty QueryTimeout = new IntegerProperty( 873 this, "mondrian.rolap.queryTimeout", 0); 874 875 /** 876 * Property that defines 877 * whether non-existent member errors should be ignored during schema 878 * load. 879 */ 880 public transient final BooleanProperty IgnoreInvalidMembers = 881 new BooleanProperty( 882 this, "mondrian.rolap.ignoreInvalidMembers", false); 883 884 /** 885 * Property that defines 886 * whether non-existent member errors should be ignored during query 887 * validation. 888 */ 889 public transient final BooleanProperty IgnoreInvalidMembersDuringQuery = 890 new BooleanProperty( 891 this, "mondrian.rolap.ignoreInvalidMembersDuringQuery", false); 892 893 /** 894 * Property that determines how a null member value is represented in the 895 * result output. 896 * <p>AS 2000 shows this as empty value 897 * <p>AS 2005 shows this as "(null)" value 898 */ 899 public transient final StringProperty NullMemberRepresentation = 900 new StringProperty(this, "mondrian.olap.NullMemberRepresentation", 901 "#null"); 902 903 /** 904 * Property that defines 905 * the iteration limit when computing an aggregate; 0 indicates unlimited. 906 */ 907 public transient final IntegerProperty IterationLimit = 908 new IntegerProperty( 909 this, "mondrian.rolap.iterationLimit", 0); 910 911 /** 912 * Property that defines 913 * whether the <code>MemoryMonitor</code> should be enabled. By 914 * default for Java5 and above it is not enabled. 915 */ 916 public transient final BooleanProperty MemoryMonitor = 917 new BooleanProperty( 918 this, "mondrian.util.memoryMonitor.enable", false); 919 920 /** 921 * Property that defines 922 * the default <code>MemoryMonitor</code> percentage threshold. 923 */ 924 public transient final IntegerProperty MemoryMonitorThreshold = 925 new IntegerProperty( 926 this, "mondrian.util.memoryMonitor.percentage.threshold", 90); 927 928 /** 929 * Property that defines 930 * the name of the class used as a memory monitor. 931 * 932 * <p>If the value is 933 * non-null, it is used by the <code>MemoryMonitorFactory</code> 934 * to create the implementation. 935 */ 936 public transient final StringProperty MemoryMonitorClass = 937 new StringProperty( 938 this, "mondrian.util.MemoryMonitor.class", null); 939 940 /** 941 * Property that defines 942 * the name of the class used to compile scalar expressions. 943 * 944 * <p>If the value is 945 * non-null, it is used by the <code>ExpCompiler.Factory</code> 946 * to create the implementation. 947 */ 948 public transient final StringProperty ExpCompilerClass = new StringProperty( 949 this, "mondrian.calc.ExpCompiler.class", null); 950 951 /** 952 * Property that defines 953 * when to apply the crossjoin optimization algorithm. 954 * 955 * <p>If a crossjoin input list's size is larger than this property's 956 * value and the axis has the "NON EMPTY" qualifier, then 957 * the crossjoin non-empty optimizer is applied. 958 * Setting this value to '0' means that for all crossjoin 959 * input lists in non-empty axes will have the optimizer applied. 960 * On the other hand, if the value is set larger than any possible 961 * list, say <code>Integer.MAX_VALUE</code>, then the optimizer 962 * will never be applied. 963 */ 964 public transient final IntegerProperty CrossJoinOptimizerSize = 965 new IntegerProperty( 966 this, "mondrian.olap.fun.crossjoin.optimizer.size", 0); 967 968 /** 969 * Property that defines 970 * the behavior of division if the denominator evaluates to zero. 971 * 972 * <p>If a division has a non-null numerator and a null denominator, 973 * it evaluates to "Infinity", which conforms to MSAS behavior. However, 974 * the old semantics of evaluating this to NULL (non MSAS-conforming), is 975 * useful in some applications. This property controls whether the 976 * result should be NULL if the denominator is Null. 977 */ 978 public transient final BooleanProperty NullDenominatorProducesNull = 979 new BooleanProperty( 980 this, "mondrian.olap.NullDenominatorProducesNull", false); 981 982 /** 983 * Property that defines 984 * whether to generate SQL queries using the <code>GROUPING SETS</code> 985 * construct for rollup. By default it is not enabled. 986 * 987 * <p>Ignored on databases which do not support the 988 * <code>GROUPING SETS</code> construct (see 989 * {@link mondrian.rolap.sql.SqlQuery.Dialect#supportsGroupingSets}). 990 */ 991 public transient final BooleanProperty EnableGroupingSets = 992 new BooleanProperty( 993 this, "mondrian.rolap.groupingsets.enable", false); 994 995 /** 996 * Property that defines whether to ignore measure when non joining 997 * dimension is in the tuple during aggregation. 998 * 999 * <p>If there are unrelated dimensions to a measure in context during 1000 * aggregation, the measure is ignored in the evaluation context. This 1001 * behaviour kicks in only if the cubeusage for this measure has 1002 * IgnoreUnrelatedDimensions attribute set to false. 1003 * 1004 * <p>For example, Gender doesn't join with [Warehouse Sales] measure. 1005 * 1006 * <p>With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=true 1007 * Warehouse Sales gets eliminated and is ignored in the aggregate value. 1008 * <blockquote> 1009 * <p> [Store Sales] + [Warehouse Sales] 1010 * SUM({Product.members * Gender.members}) 7,913,333.82 1011 * </blockquote> 1012 * <p>With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=false 1013 * Warehouse Sales with Gender All level member contributes to the aggregate 1014 * value. 1015 * <blockquote> 1016 * <p> [Store Sales] + [Warehouse Sales] 1017 * SUM({Product.members * Gender.members}) 9,290,730.03 1018 * </blockquote> 1019 * <p>On a report where Gender M, F and All members exist a user will see a 1020 * large aggregated value compared to the aggregated value that can be 1021 * arrived at by suming up values against Gender M and F. This can be 1022 * confusing to the user. This feature can be used to eliminate such a 1023 * situation. 1024 */ 1025 public transient final BooleanProperty IgnoreMeasureForNonJoiningDimension = 1026 new BooleanProperty( 1027 this, 1028 "mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension", 1029 false); 1030 1031 /** 1032 * Property determines if elements of dimension (levels, hierarchies, members) 1033 * need to be prefixed with dimension name in MDX query. 1034 * For example when the property is true, the following queries 1035 * will error out. The same queries will work when this property 1036 * is set to false. 1037 * <blockquote> 1038 * <p> 1039 * select {[M]} on 0 from sales 1040 * <p> 1041 * select {[USA]} on 0 from sales 1042 * <p> 1043 * select {[USA].[CA].[Santa Monica]} on 0 from sales 1044 * </blockquote> 1045 * <p> 1046 * When the property is set to true, any query where elements are 1047 * prefixed with dimension name as below will work 1048 * <blockquote> 1049 * <p> 1050 * select {[Gender].[F]} on 0 from sales 1051 * <p> 1052 * select {[Customers].[Santa Monica]} on 0 from sales 1053 * </blockquote> 1054 * <p> 1055 * Please note that this property does not govern the behaviour where in 1056 * <blockquote> 1057 * <p> 1058 * [Gender].[M] 1059 * </blockquote> 1060 * <p> 1061 * is resolved into a fully qualified 1062 * <blockquote> 1063 * <p> 1064 * [Gender].[All Gender].[M] 1065 * </blockquote> 1066 * <p> 1067 * In a scenario where the schema is very large and dimensions have large 1068 * number of members a MDX query that has a invalid member in it will cause 1069 * mondrian to to go through all the dimensions, levels, hierarchies, members 1070 * and properties trying to resolve the element name. This behaviour consumes 1071 * considerable time and resources on the server. Setting this property to 1072 * true will make it fail fast in a scenario where it is desirable 1073 */ 1074 public transient final BooleanProperty NeedDimensionPrefix = 1075 new BooleanProperty( 1076 this, "mondrian.olap.elements.NeedDimensionPrefix", false); 1077 1078 /** 1079 * Property that determines whether to cache RolapCubeMember objects, 1080 * each of which associates a member of a shared hierarchy with a 1081 * particular cube in which it is being used. 1082 * 1083 * <p>The default is {@code true}, that is, use a cache. If you wish to use 1084 * the member cache control aspects of {@link mondrian.olap.CacheControl}, 1085 * you must set this property to {@code false}.</p> 1086 * 1087 * <p>In future, we plan to make RolapCubeMember more lightweight to 1088 * construct, and we will probably obsolete this cache and this 1089 * property.</p> 1090 */ 1091 public transient final BooleanProperty EnableRolapCubeMemberCache = 1092 new BooleanProperty( 1093 this, "mondrian.rolap.EnableRolapCubeMemberCache", true); 1094 1095 /** 1096 * Property that controls the behavior of 1097 * {@link Property#SOLVE_ORDER solve order} of calculated members and sets. 1098 * 1099 * <p>Valid values are "absolute" and "scoped" (the default). See 1100 * {@link SolveOrderModeEnum} for details.</p> 1101 */ 1102 public transient final StringProperty SolveOrderMode = 1103 new StringProperty( 1104 this, "mondrian.rolap.SolveOrderMode", SolveOrderModeEnum.ABSOLUTE.name()); 1105 1106 /** 1107 * Strategies for applying solve order, exposed via the property 1108 * {@link MondrianProperties#SolveOrderMode}. 1109 */ 1110 public enum SolveOrderModeEnum { 1111 1112 /** 1113 * The SOLVE_ORDER value is absolute regardless of 1114 * where it is defined; e.g. a query defined calculated 1115 * member with a SOLVE_ORDER of 1 always takes precedence 1116 * over a cube defined value of 2. 1117 * 1118 * <p>Compatible with Analysis Services 2000, and default behavior 1119 * up to mondrian-3.0.3. 1120 */ 1121 ABSOLUTE, 1122 1123 /** 1124 * Cube calculated members are resolved before any session 1125 * scope calculated members, and session scope members are 1126 * resolved before any query defined calculation. The 1127 * SOLVE_ORDER value only applies within the scope in which 1128 * it was defined. 1129 * 1130 * <p>Compatible with Analysis Services 2005, and default behavior 1131 * from mondrian-3.0.4 and later. 1132 */ 1133 SCOPED; 1134 } 1135 } 1136 1137 // End MondrianProperties.java