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 008// All Rights Reserved. 009*/ 010package mondrian.olap4j; 011 012import mondrian.calc.ResultStyle; 013import mondrian.olap.*; 014import mondrian.rolap.RolapConnection; 015import mondrian.server.*; 016import mondrian.util.Pair; 017 018import org.olap4j.*; 019import org.olap4j.layout.RectangularCellSetFormatter; 020import org.olap4j.mdx.*; 021 022import java.io.PrintWriter; 023import java.io.StringWriter; 024import java.sql.*; 025import java.util.Collections; 026import java.util.List; 027 028/** 029 * Implementation of {@link org.olap4j.OlapStatement} 030 * for the Mondrian OLAP engine. 031 * 032 * @author jhyde 033 * @since May 24, 2007 034 */ 035abstract class MondrianOlap4jStatement 036 extends StatementImpl 037 implements OlapStatement, mondrian.server.Statement 038{ 039 final MondrianOlap4jConnection olap4jConnection; 040 private boolean closed; 041 042 /** 043 * Support for {@link #closeOnCompletion()} method. 044 */ 045 protected boolean closeOnCompletion; 046 047 /** 048 * Current cell set, or null if the statement is not executing anything. 049 * Any method which modifies this member must synchronize 050 * on the MondrianOlap4jStatement. 051 */ 052 MondrianOlap4jCellSet openCellSet; 053 054 MondrianOlap4jStatement( 055 MondrianOlap4jConnection olap4jConnection) 056 { 057 assert olap4jConnection != null; 058 this.olap4jConnection = olap4jConnection; 059 this.closed = false; 060 } 061 062 // implement Statement 063 064 public ResultSet executeQuery(String mdx) throws SQLException { 065 return executeQuery2(mdx, false, null, null); 066 } 067 068 ResultSet executeQuery2( 069 String mdx, 070 boolean advanced, 071 String tabFields, 072 int[] rowCountSlot) throws SQLException 073 { 074 if (advanced) { 075 // REVIEW: I removed 'executeDrillThroughAdvanced' in the cleanup. 076 // Do we still need it? 077 throw new UnsupportedOperationException(); 078 } 079 QueryPart parseTree; 080 try { 081 parseTree = 082 olap4jConnection.getMondrianConnection().parseStatement(mdx); 083 } catch (MondrianException e) { 084 throw olap4jConnection.helper.createException( 085 "mondrian gave exception while parsing query", e); 086 } 087 if (parseTree instanceof DrillThrough) { 088 DrillThrough drillThrough = (DrillThrough) parseTree; 089 final Query query = drillThrough.getQuery(); 090 query.setResultStyle(ResultStyle.LIST); 091 setQuery(query); 092 CellSet cellSet = executeOlapQueryInternal(query, null); 093 final List<Integer> coords = Collections.nCopies( 094 cellSet.getAxes().size(), 0); 095 final MondrianOlap4jCell cell = 096 (MondrianOlap4jCell) cellSet.getCell(coords); 097 098 ResultSet resultSet = 099 cell.drillThroughInternal( 100 drillThrough.getMaxRowCount(), 101 drillThrough.getFirstRowOrdinal(), 102 drillThrough.getReturnList(), 103 true, 104 null, 105 rowCountSlot); 106 if (resultSet == null) { 107 throw new OlapException( 108 "Cannot do DrillThrough operation on the cell"); 109 } 110 return resultSet; 111 } else if (parseTree instanceof Explain) { 112 String plan = explainInternal(((Explain) parseTree).getQuery()); 113 return olap4jConnection.factory.newFixedResultSet( 114 olap4jConnection, 115 Collections.singletonList("PLAN"), 116 Collections.singletonList( 117 Collections.<Object>singletonList(plan))); 118 } else { 119 throw olap4jConnection.helper.createException( 120 "Query does not have relational result. Use a DRILLTHROUGH " 121 + "query, or execute using the executeOlapQuery method."); 122 } 123 } 124 125 private String explainInternal(QueryPart query) { 126 final StringWriter sw = new StringWriter(); 127 final PrintWriter pw = new PrintWriter(sw); 128 query.explain(pw); 129 pw.flush(); 130 return sw.toString(); 131 } 132 133 public int executeUpdate(String sql) throws SQLException { 134 throw new UnsupportedOperationException(); 135 } 136 137 public synchronized void close() { 138 if (!closed) { 139 closed = true; 140 olap4jConnection.mondrianServer.removeStatement(this); 141 if (openCellSet != null) { 142 MondrianOlap4jCellSet c = openCellSet; 143 openCellSet = null; 144 c.close(); 145 } 146 } 147 } 148 149 public int getMaxFieldSize() throws SQLException { 150 throw new UnsupportedOperationException(); 151 } 152 153 public void setMaxFieldSize(int max) throws SQLException { 154 throw new UnsupportedOperationException(); 155 } 156 157 public int getMaxRows() throws SQLException { 158 throw new UnsupportedOperationException(); 159 } 160 161 public void setMaxRows(int max) throws SQLException { 162 throw new UnsupportedOperationException(); 163 } 164 165 public void setEscapeProcessing(boolean enable) throws SQLException { 166 throw new UnsupportedOperationException(); 167 } 168 169 public int getQueryTimeout() throws SQLException { 170 long timeoutSeconds = getQueryTimeoutMillis() / 1000; 171 if (timeoutSeconds > Integer.MAX_VALUE) { 172 return Integer.MAX_VALUE; 173 } 174 if (timeoutSeconds == 0 && getQueryTimeoutMillis() > 0) { 175 // Don't return timeout=0 if e.g. timeoutMillis=500. 0 is special. 176 return 1; 177 } 178 return (int) timeoutSeconds; 179 } 180 181 public void setQueryTimeout(int seconds) throws SQLException { 182 if (seconds < 0) { 183 throw olap4jConnection.helper.createException( 184 "illegal timeout value " + seconds); 185 } 186 setQueryTimeoutMillis(seconds * 1000); 187 } 188 189 public synchronized void cancel() throws SQLException { 190 if (openCellSet != null) { 191 openCellSet.cancel(); 192 } 193 } 194 195 public SQLWarning getWarnings() throws SQLException { 196 throw new UnsupportedOperationException(); 197 } 198 199 public void clearWarnings() throws SQLException { 200 throw new UnsupportedOperationException(); 201 } 202 203 public void setCursorName(String name) throws SQLException { 204 throw new UnsupportedOperationException(); 205 } 206 207 public boolean execute(String sql) throws SQLException { 208 throw new UnsupportedOperationException(); 209 } 210 211 public ResultSet getResultSet() throws SQLException { 212 // NOTE: cell set becomes visible in this member while 213 // executeOlapQueryInternal is still in progress, and before it has 214 // finished executing. Its internal state may not be ready for API 215 // calls. JDBC never claims to be thread-safe! (Except for calls to the 216 // cancel method.) It is not possible to synchronize, because it would 217 // block 'cancel'. 218 return openCellSet; 219 } 220 221 public int getUpdateCount() throws SQLException { 222 throw new UnsupportedOperationException(); 223 } 224 225 public boolean getMoreResults() throws SQLException { 226 throw new UnsupportedOperationException(); 227 } 228 229 public void setFetchDirection(int direction) throws SQLException { 230 throw new UnsupportedOperationException(); 231 } 232 233 public int getFetchDirection() throws SQLException { 234 throw new UnsupportedOperationException(); 235 } 236 237 public void setFetchSize(int rows) throws SQLException { 238 throw new UnsupportedOperationException(); 239 } 240 241 public int getFetchSize() throws SQLException { 242 throw new UnsupportedOperationException(); 243 } 244 245 public int getResultSetConcurrency() throws SQLException { 246 throw new UnsupportedOperationException(); 247 } 248 249 public int getResultSetType() throws SQLException { 250 throw new UnsupportedOperationException(); 251 } 252 253 public void addBatch(String sql) throws SQLException { 254 throw new UnsupportedOperationException(); 255 } 256 257 public void clearBatch() throws SQLException { 258 throw new UnsupportedOperationException(); 259 } 260 261 public int[] executeBatch() throws SQLException { 262 throw new UnsupportedOperationException(); 263 } 264 265 public OlapConnection getConnection() { 266 return olap4jConnection; 267 } 268 269 public boolean getMoreResults(int current) throws SQLException { 270 throw new UnsupportedOperationException(); 271 } 272 273 public ResultSet getGeneratedKeys() throws SQLException { 274 throw new UnsupportedOperationException(); 275 } 276 277 public int executeUpdate( 278 String sql, int autoGeneratedKeys) throws SQLException 279 { 280 throw new UnsupportedOperationException(); 281 } 282 283 public int executeUpdate( 284 String sql, int columnIndexes[]) throws SQLException 285 { 286 throw new UnsupportedOperationException(); 287 } 288 289 public int executeUpdate( 290 String sql, String columnNames[]) throws SQLException 291 { 292 throw new UnsupportedOperationException(); 293 } 294 295 public boolean execute( 296 String sql, int autoGeneratedKeys) throws SQLException 297 { 298 throw new UnsupportedOperationException(); 299 } 300 301 public boolean execute( 302 String sql, int columnIndexes[]) throws SQLException 303 { 304 throw new UnsupportedOperationException(); 305 } 306 307 public boolean execute( 308 String sql, String columnNames[]) throws SQLException 309 { 310 throw new UnsupportedOperationException(); 311 } 312 313 public int getResultSetHoldability() throws SQLException { 314 throw new UnsupportedOperationException(); 315 } 316 317 public boolean isClosed() throws SQLException { 318 return closed; 319 } 320 321 public void setPoolable(boolean poolable) throws SQLException { 322 throw new UnsupportedOperationException(); 323 } 324 325 public boolean isPoolable() throws SQLException { 326 throw new UnsupportedOperationException(); 327 } 328 329 // implement Wrapper 330 331 public <T> T unwrap(Class<T> iface) throws SQLException { 332 if (iface.isInstance(this)) { 333 return iface.cast(this); 334 } 335 throw olap4jConnection.helper.createException( 336 "does not implement '" + iface + "'"); 337 } 338 339 public boolean isWrapperFor(Class<?> iface) throws SQLException { 340 return iface.isInstance(this); 341 } 342 343 // implement OlapStatement 344 345 public CellSet executeOlapQuery(final String mdx) throws OlapException { 346 final Pair<Query, MondrianOlap4jCellSetMetaData> pair = parseQuery(mdx); 347 return executeOlapQueryInternal(pair.left, pair.right); 348 } 349 350 protected Pair<Query, MondrianOlap4jCellSetMetaData> 351 parseQuery(final String mdx) 352 throws OlapException 353 { 354 try { 355 final RolapConnection mondrianConnection = getMondrianConnection(); 356 return Locus.execute( 357 mondrianConnection, 358 "Parsing query", 359 new Locus.Action<Pair<Query, MondrianOlap4jCellSetMetaData>>() { 360 public Pair<Query, MondrianOlap4jCellSetMetaData> execute() 361 { 362 final Query query = 363 (Query) mondrianConnection.parseStatement( 364 MondrianOlap4jStatement.this, 365 mdx, 366 null, 367 false); 368 final MondrianOlap4jCellSetMetaData cellSetMetaData = 369 new MondrianOlap4jCellSetMetaData( 370 MondrianOlap4jStatement.this, query); 371 return Pair.of(query, cellSetMetaData); 372 } 373 }); 374 } catch (MondrianException e) { 375 throw olap4jConnection.helper.createException( 376 "mondrian gave exception while parsing query", e); 377 } 378 } 379 380 /** 381 * Executes a parsed query, closing any previously open cellset. 382 * 383 * 384 * @param query Parsed query 385 * @param cellSetMetaData Cell set metadata 386 * @return Cell set 387 * @throws OlapException if a database error occurs 388 */ 389 protected CellSet executeOlapQueryInternal( 390 Query query, 391 MondrianOlap4jCellSetMetaData cellSetMetaData) throws OlapException 392 { 393 // Close the previous open CellSet, if there is one. 394 synchronized (this) { 395 if (openCellSet != null) { 396 final MondrianOlap4jCellSet cs = openCellSet; 397 openCellSet = null; 398 try { 399 cs.close(); 400 } catch (Exception e) { 401 throw olap4jConnection.helper.createException( 402 null, "Error while closing previous CellSet", e); 403 } 404 } 405 406 if (olap4jConnection.preferList) { 407 query.setResultStyle(ResultStyle.LIST); 408 } 409 this.query = query; 410 openCellSet = olap4jConnection.factory.newCellSet(this); 411 } 412 // Release the monitor before executing, to give another thread the 413 // opportunity to call cancel. 414 try { 415 openCellSet.execute(); 416 } catch (QueryCanceledException e) { 417 throw olap4jConnection.helper.createException( 418 "Query canceled", e); 419 } catch (QueryTimeoutException e) { 420 throw olap4jConnection.helper.createException( 421 e.getMessage(), e); 422 } catch (MondrianException e) { 423 throw olap4jConnection.helper.createException( 424 "mondrian gave exception while executing query", e); 425 } 426 return openCellSet; 427 } 428 429 @Override 430 public void start(Execution execution) { 431 super.start(openCellSet); 432 } 433 434 public CellSet executeOlapQuery(SelectNode selectNode) 435 throws OlapException 436 { 437 final String mdx = toString(selectNode); 438 return executeOlapQuery(mdx); 439 } 440 441 public void addListener( 442 CellSetListener.Granularity granularity, 443 CellSetListener cellSetListener) throws OlapException 444 { 445 // Cell set listener API not supported in this version of mondrian. 446 throw new UnsupportedOperationException(); 447 } 448 449 /** 450 * Converts a {@link org.olap4j.mdx.ParseTreeNode} to MDX string. 451 * 452 * @param node Parse tree node 453 * @return MDX text 454 */ 455 private static String toString(ParseTreeNode node) { 456 StringWriter sw = new StringWriter(); 457 PrintWriter pw = new PrintWriter(sw); 458 ParseTreeWriter parseTreeWriter = new ParseTreeWriter(pw); 459 node.unparse(parseTreeWriter); 460 pw.flush(); 461 return sw.toString(); 462 } 463 464 public RolapConnection getMondrianConnection() { 465 try { 466 return olap4jConnection.getMondrianConnection(); 467 } catch (OlapException e) { 468 throw new RuntimeException(e); 469 } 470 } 471 472 /** 473 * Called by each child result set (most likely a cell set) when it is 474 * closed. 475 * 476 * @param resultSet Result set or cell set 477 */ 478 void onResultSetClose(ResultSet resultSet) { 479 if (closeOnCompletion) { 480 close(); 481 } 482 } 483} 484 485// End MondrianOlap4jStatement.java