diff --git a/mondrian/src/main/java/mondrian/olap/ConnectionBase.java b/mondrian/src/main/java/mondrian/olap/ConnectionBase.java index b8c23f3b70..a361a631bb 100644 --- a/mondrian/src/main/java/mondrian/olap/ConnectionBase.java +++ b/mondrian/src/main/java/mondrian/olap/ConnectionBase.java @@ -1,13 +1,26 @@ /* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2001-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara and others -// All Rights Reserved. -*/ + * This software is subject to the terms of the Eclipse Public License v1.0 + * Agreement, available at the following URL: + * http://www.eclipse.org/legal/epl-v10.html. + * You must accept the terms of that agreement to use this software. + * + * Copyright (c) 2002-2017 Hitachi Vantara.. All rights reserved. + * + * ---- All changes after Fork in 2023 ------------------------ + * + * Project: Eclipse daanse + * + * Copyright (c) 2023 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors after Fork in 2023: + * SmartCity Jena - initial + */ package mondrian.olap; @@ -25,13 +38,12 @@ import org.eclipse.daanse.olap.api.function.FunctionService; import org.eclipse.daanse.olap.api.query.ExpressionProvider; import org.eclipse.daanse.olap.api.query.QueryProvider; +import org.eclipse.daanse.olap.api.query.component.Query; import org.eclipse.daanse.olap.api.query.component.QueryComponent; import org.eclipse.daanse.olap.query.base.ExpressionProviderImpl; import org.eclipse.daanse.olap.query.base.QueryProviderImpl; -import org.eclipse.daanse.olap.query.component.QueryImpl; import org.eclipse.daanse.sql.guard.api.SqlGuard; import org.eclipse.daanse.sql.guard.api.SqlGuardFactory; -import org.eclipse.daanse.sql.guard.api.elements.DatabaseCatalog; import org.eclipse.daanse.sql.guard.api.exception.GuardException; import org.eclipse.daanse.sql.guard.api.exception.UnparsableStatementGuardException; import org.slf4j.Logger; @@ -63,8 +75,8 @@ protected ConnectionBase() { @Override - public QueryImpl parseQuery(String query) { - return (QueryImpl) parseStatement(query); + public Query parseQuery(String query) { + return (Query) parseStatement(query); } /** diff --git a/mondrian/src/main/java/mondrian/olap/IdBatchResolver.java b/mondrian/src/main/java/mondrian/olap/IdBatchResolver.java index af6f885812..2b8e97e578 100644 --- a/mondrian/src/main/java/mondrian/olap/IdBatchResolver.java +++ b/mondrian/src/main/java/mondrian/olap/IdBatchResolver.java @@ -1,12 +1,27 @@ /* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2006-2017 Hitachi Vantara and others -// All Rights Reserved. + * This software is subject to the terms of the Eclipse Public License v1.0 + * Agreement, available at the following URL: + * http://www.eclipse.org/legal/epl-v10.html. + * You must accept the terms of that agreement to use this software. + * + * Copyright (c) 2002-2017 Hitachi Vantara.. All rights reserved. + * + * ---- All changes after Fork in 2023 ------------------------ + * + * Project: Eclipse daanse + * + * Copyright (c) 2023 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors after Fork in 2023: + * SmartCity Jena - initial */ + package mondrian.olap; import java.util.ArrayList; @@ -34,14 +49,14 @@ import org.eclipse.daanse.olap.api.query.component.DimensionExpression; import org.eclipse.daanse.olap.api.query.component.Expression; import org.eclipse.daanse.olap.api.query.component.Formula; +import org.eclipse.daanse.olap.api.query.component.HierarchyExpression; import org.eclipse.daanse.olap.api.query.component.Id; import org.eclipse.daanse.olap.api.query.component.MemberExpression; +import org.eclipse.daanse.olap.api.query.component.Query; import org.eclipse.daanse.olap.api.query.component.QueryAxis; import org.eclipse.daanse.olap.api.query.component.QueryComponent; import org.eclipse.daanse.olap.api.query.component.visit.QueryComponentVisitor; -import org.eclipse.daanse.olap.query.component.HierarchyExpressionImpl; import org.eclipse.daanse.olap.query.component.IdImpl; -import org.eclipse.daanse.olap.query.component.QueryImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,7 +82,7 @@ public final class IdBatchResolver { static final Logger LOGGER = LoggerFactory.getLogger(IdBatchResolver.class); - private final QueryImpl query; + private final Query query; private final Formula[] formulas; private final QueryAxis[] axes; private final Cube cube; @@ -87,7 +102,7 @@ public final class IdBatchResolver { // first on segment length (shortest to longest), then alphabetically. private SortedSet identifiers = new TreeSet<>(new IdComparator()); - public IdBatchResolver(QueryImpl query) { + public IdBatchResolver(Query query) { this.query = query; formulas = query.getFormulas(); axes = query.getAxes(); @@ -323,7 +338,7 @@ private Member getMemberFromExp(Expression exp) { if (hier.hasAll()) { return hier.getAllMember(); } - } else if (exp instanceof HierarchyExpressionImpl hierarchyExpr) { + } else if (exp instanceof HierarchyExpression hierarchyExpr) { Hierarchy hier = hierarchyExpr .getHierarchy(); if (hier.hasAll()) { diff --git a/mondrian/src/main/java/mondrian/recorder/AbstractRecorder.java b/mondrian/src/main/java/mondrian/recorder/AbstractRecorder.java index 4b9826da7e..1e6ebbe665 100644 --- a/mondrian/src/main/java/mondrian/recorder/AbstractRecorder.java +++ b/mondrian/src/main/java/mondrian/recorder/AbstractRecorder.java @@ -1,13 +1,27 @@ /* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2005-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara and others -// All Rights Reserved. -*/ + * This software is subject to the terms of the Eclipse Public License v1.0 + * Agreement, available at the following URL: + * http://www.eclipse.org/legal/epl-v10.html. + * You must accept the terms of that agreement to use this software. + * + * Copyright (C) 2005-2005 Julian Hyde + * Copyright (c) 2002-2017 Hitachi Vantara.. All rights reserved. + * + * ---- All changes after Fork in 2023 ------------------------ + * + * Project: Eclipse daanse + * + * Copyright (c) 2023 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors after Fork in 2023: + * SmartCity Jena - initial + */ package mondrian.recorder; @@ -23,20 +37,14 @@ */ public abstract class AbstractRecorder implements MessageRecorder { - private final static String tooManyMessageRecorderErrors = - "Context ''{0}'': Exceeded number of allowed errors ''{1,number}''"; - private final static String forceMessageRecorderError = - "Context ''{0}'': Client forcing return with errors ''{1,number}''"; + private final static String tooManyMessageRecorderErrors = "Context ''{0}'': Exceeded number of allowed errors ''{1,number}''"; + private final static String forceMessageRecorderError = "Context ''{0}'': Client forcing return with errors ''{1,number}''"; /** * Helper method to format a message and write to logger. */ - public static void logMessage( - final String context, - final String msg, - final MsgType msgType, - final Logger logger) - { + public static void logMessage(final String context, final String msg, final MessageType msgType, + final Logger logger) { StringBuilder buf = new StringBuilder(64); buf.append(context); buf.append(": "); @@ -53,15 +61,12 @@ public static void logMessage( logger.error(logMsg); break; default: - logger.warn( - "Unknown message type enum \"{}\" for message: {}", msgType, logMsg); + logger.warn("Unknown message type enum \"{}\" for message: {}", msgType, logMsg); } } - enum MsgType { - INFO, - WARN, - ERROR + enum MessageType { + INFO, WARN, ERROR } public static final int DEFAULT_MSG_LIMIT = 10; @@ -88,7 +93,7 @@ protected AbstractRecorder(final int errorMsgLimit) { * Resets this MessageRecorder. */ @Override - public void clear() { + public void clear() { errorMsgCount = 0; warningMsgCount = 0; infoMsgCount = 0; @@ -98,27 +103,27 @@ public void clear() { } @Override - public long getStartTimeMillis() { + public long getStartTimeMillis() { return this.startTime; } @Override - public long getRunTimeMillis() { + public long getRunTimeMillis() { return (System.currentTimeMillis() - this.startTime); } @Override - public boolean hasInformation() { + public boolean hasInformation() { return (infoMsgCount > 0); } @Override - public boolean hasWarnings() { + public boolean hasWarnings() { return (warningMsgCount > 0); } @Override - public boolean hasErrors() { + public boolean hasErrors() { return (errorMsgCount > 0); } @@ -135,7 +140,7 @@ public int getErrorCount() { } @Override - public String getContext() { + public String getContext() { // heavy weight if (contextMsgCache == null) { final StringBuilder buf = new StringBuilder(); @@ -152,100 +157,83 @@ public String getContext() { } @Override - public void pushContextName(final String name) { + public void pushContextName(final String name) { // light weight contexts.add(name); contextMsgCache = null; } @Override - public void popContextName() { + public void popContextName() { // light weight contexts.remove(contexts.size() - 1); contextMsgCache = null; } @Override - public void throwRTException() throws RecorderException { + public void throwRTException() throws RecorderException { if (hasErrors()) { - final String errorMsg = - MessageFormat.format(forceMessageRecorderError, - getContext(), - errorMsgCount); + final String errorMsg = MessageFormat.format(forceMessageRecorderError, getContext(), errorMsgCount); throw new RecorderException(errorMsg); } } @Override - public void reportError(final Exception ex) - throws RecorderException - { + public void reportError(final Exception ex) throws RecorderException { reportError(ex, null); } @Override - public void reportError(final Exception ex, final Object info) - throws RecorderException - { + public void reportError(final Exception ex, final Object info) throws RecorderException { reportError(ex.toString(), info); } @Override - public void reportError(final String msg) - throws RecorderException - { + public void reportError(final String msg) throws RecorderException { reportError(msg, null); } @Override - public void reportError(final String msg, final Object info) - throws RecorderException - { + public void reportError(final String msg, final Object info) throws RecorderException { errorMsgCount++; - recordMessage(msg, info, MsgType.ERROR); + recordMessage(msg, info, MessageType.ERROR); if (errorMsgCount >= errorMsgLimit) { - final String errorMsg = - MessageFormat.format(tooManyMessageRecorderErrors, - getContext(), + final String errorMsg = MessageFormat.format(tooManyMessageRecorderErrors, getContext(), String.valueOf(errorMsgCount)); throw new RecorderException(errorMsg); } } @Override - public void reportWarning(final String msg) { + public void reportWarning(final String msg) { reportWarning(msg, null); } @Override - public void reportWarning(final String msg, final Object info) { + public void reportWarning(final String msg, final Object info) { warningMsgCount++; - recordMessage(msg, info, MsgType.WARN); + recordMessage(msg, info, MessageType.WARN); } @Override - public void reportInfo(final String msg) { + public void reportInfo(final String msg) { reportInfo(msg, null); } @Override - public void reportInfo(final String msg, final Object info) { + public void reportInfo(final String msg, final Object info) { infoMsgCount++; - recordMessage(msg, info, MsgType.INFO); + recordMessage(msg, info, MessageType.INFO); } /** - * Handles a message. - * Classes implementing this abstract class must provide an implemention - * of this method; it receives all warning/error messages. + * Handles a message. Classes implementing this abstract class must provide an + * implemention of this method; it receives all warning/error messages. * - * @param msg the error or warning message. - * @param info the information Object which might be null. + * @param msg the error or warning message. + * @param info the information Object which might be null. * @param msgType one of the message type enum values */ - protected abstract void recordMessage( - String msg, - Object info, - MsgType msgType); + protected abstract void recordMessage(String msg, Object info, MessageType msgType); } diff --git a/mondrian/src/main/java/mondrian/recorder/ListRecorder.java b/mondrian/src/main/java/mondrian/recorder/ListRecorder.java index 171a7417e6..13da3fb95e 100644 --- a/mondrian/src/main/java/mondrian/recorder/ListRecorder.java +++ b/mondrian/src/main/java/mondrian/recorder/ListRecorder.java @@ -1,13 +1,27 @@ /* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2005-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara and others -// All Rights Reserved. -*/ + * This software is subject to the terms of the Eclipse Public License v1.0 + * Agreement, available at the following URL: + * http://www.eclipse.org/legal/epl-v10.html. + * You must accept the terms of that agreement to use this software. + * + * Copyright (C) 2005-2005 Julian Hyde + * Copyright (c) 2002-2017 Hitachi Vantara.. All rights reserved. + * + * ---- All changes after Fork in 2023 ------------------------ + * + * Project: Eclipse daanse + * + * Copyright (c) 2023 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors after Fork in 2023: + * SmartCity Jena - initial + */ package mondrian.recorder; @@ -18,9 +32,9 @@ import org.slf4j.Logger; /** - * Implementation of {@link MessageRecorder} that records each message - * in a {@link List}. The calling code can then access the list and take - * actions as needed. + * Implementation of {@link MessageRecorder} that records each message in a + * {@link List}. The calling code can then access the list and take actions as + * needed. */ public class ListRecorder extends AbstractRecorder { @@ -35,7 +49,7 @@ public ListRecorder() { } @Override - public void clear() { + public void clear() { super.clear(); errorList.clear(); warnList.clear(); @@ -55,11 +69,7 @@ public Iterator getInfoEntries() { } @Override - protected void recordMessage( - final String msg, - final Object info, - final MsgType msgType) - { + protected void recordMessage(final String msg, final Object info, final MessageType msgType) { String context = getContext(); Entry e = new Entry(context, msg, msgType, info); @@ -74,12 +84,8 @@ protected void recordMessage( errorList.add(e); break; default: - e = new Entry( - context, - new StringBuilder("Unknown message type enum \"").append(msgType) - .append("\" for message: ").append(msg).toString(), - MsgType.WARN, - info); + e = new Entry(context, new StringBuilder("Unknown message type enum \"").append(msgType) + .append("\" for message: ").append(msg).toString(), MessageType.WARN, info); warnList.add(e); } } @@ -109,49 +115,15 @@ static void logMessage(Iterator it, Logger logger) { } } - static void logMessage( - final Entry e, - final Logger logger) - { - logMessage(e.getContext(), e.getMessage(), e.getMsgType(), logger); + static void logMessage(final Entry e, final Logger logger) { + logMessage(e.context(), e.message(), e.msgType(), logger); } /** - * Entry is a Info, Warning or Error message. This is the object stored - * in the Lists MessageRecorder's info, warning and error message lists. + * Entry is a Info, Warning or Error message. This is the object stored in the + * Lists MessageRecorder's info, warning and error message lists. */ - public static class Entry { - private final String context; - private final String msg; - private final MsgType msgType; - private final Object info; - - private Entry( - final String context, - final String msg, - final MsgType msgType, - final Object info) - { - this.context = context; - this.msg = msg; - this.msgType = msgType; - this.info = info; - } + public static record Entry(String context, String message, MessageType msgType, Object info) { - public String getContext() { - return context; - } - - public String getMessage() { - return msg; - } - - public MsgType getMsgType() { - return msgType; - } - - public Object getInfo() { - return info; - } } } diff --git a/mondrian/src/main/java/mondrian/recorder/LoggerRecorder.java b/mondrian/src/main/java/mondrian/recorder/LoggerRecorder.java deleted file mode 100644 index fe269586f3..0000000000 --- a/mondrian/src/main/java/mondrian/recorder/LoggerRecorder.java +++ /dev/null @@ -1,38 +0,0 @@ -/* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2005-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara and others -// All Rights Reserved. -*/ - -package mondrian.recorder; - -import org.slf4j.Logger; - -/** - * Implementation of {@link MessageRecorder} that writes to a - * {@link Logger logger}. - * - * @author Richard M. Emberson - */ -public class LoggerRecorder extends AbstractRecorder { - private final Logger logger; - - public LoggerRecorder(final Logger logger) { - this.logger = logger; - } - - @Override - protected void recordMessage( - final String msg, - final Object info, - final MsgType msgType) - { - String context = getContext(); - logMessage(context, msg, msgType, logger); - } -} diff --git a/mondrian/src/main/java/mondrian/recorder/MessageRecorder.java b/mondrian/src/main/java/mondrian/recorder/MessageRecorder.java index b7fe85d708..857fe84933 100644 --- a/mondrian/src/main/java/mondrian/recorder/MessageRecorder.java +++ b/mondrian/src/main/java/mondrian/recorder/MessageRecorder.java @@ -1,25 +1,40 @@ /* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2005-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara and others -// All Rights Reserved. -*/ + * This software is subject to the terms of the Eclipse Public License v1.0 + * Agreement, available at the following URL: + * http://www.eclipse.org/legal/epl-v10.html. + * You must accept the terms of that agreement to use this software. + * + * Copyright (C) 2005-2005 Julian Hyde + * Copyright (c) 2002-2017 Hitachi Vantara.. All rights reserved. + * + * ---- All changes after Fork in 2023 ------------------------ + * + * Project: Eclipse daanse + * + * Copyright (c) 2023 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors after Fork in 2023: + * SmartCity Jena - initial + */ package mondrian.recorder; /** - * Records warnings and errors during the processing of a task. - * Contexts can be added and removed. - * This allows one to collect more than one warning/error, keep processing, - * and then the code that initiated the processing can determine what to do - * with the warnings/errors if they exist. + * Records warnings and errors during the processing of a task. Contexts can be + * added and removed. This allows one to collect more than one warning/error, + * keep processing, and then the code that initiated the processing can + * determine what to do with the warnings/errors if they exist. *

* A typical usage might be: - *


+ * 
+ * 
+ * 
  *    void process(MessageRecorder msgRecorder) {
  *        msgRecorder.pushContextName(getName());
  *        try {
@@ -33,7 +48,8 @@
  *              msgRecorder.popContextName();
  *        }
  *    }
- * 
+ * + *
*

* Implementations must provide the means for extracting the error/warning * messages. @@ -41,9 +57,11 @@ * Code that is processing should not catch the MessageRecorder.RTException. * This Exception is thrown by the MessageRecorder when too many errors have * been seen. Throwing this Exception is the mechanism used to stop processing - * and return to the initiating code. The initiating code should expect to - * catch the MessageRecorder.RTException Exception. - *


+ * and return to the initiating code. The initiating code should expect to catch
+ * the MessageRecorder.RTException Exception.
+ * 
+ * 
+ * 
  *    void initiatingCode(MessageRecorder msgRecorder) {
  *      // get MessageRecorder implementation
  *      MessageRecorder msgRecorder = ....
@@ -58,11 +76,12 @@
  *          // handle warnings
  *      }
  *    }
- * 
+ * + *
*

- * The reporting methods all have variations that take an "info" Object. - * This can be used to pass something, beyond a text message, from the point - * of warning/error to the initiating code. + * The reporting methods all have variations that take an "info" Object. This + * can be used to pass something, beyond a text message, from the point of + * warning/error to the initiating code. *

* Concerning logging, it is a rule that a message, if logged by the code * creating the MessageRecorder implementation, is logged at is reporting level, @@ -75,15 +94,15 @@ public interface MessageRecorder { /** - * Clear all context, warnings and errors from the MessageRecorder. - * After calling this method the MessageRecorder implemenation should - * be in the same state as if it were just constructed. + * Clear all context, warnings and errors from the MessageRecorder. After + * calling this method the MessageRecorder implemenation should be in the same + * state as if it were just constructed. */ void clear(); /** - * Get the time when the MessageRecorder was created or the last time that - * the clear method was called. + * Get the time when the MessageRecorder was created or the last time that the + * clear method was called. * * @return the start time */ @@ -136,11 +155,11 @@ public interface MessageRecorder { void popContextName(); /** - * This simply throws a RTException. A client calls this if 1) there is one - * or more error messages reported and 2) the client wishes to stop - * processing. Implementations of this method should only throw the - * RTException if there have been errors reported - if there are no errors, - * then this method does nothing. + * This simply throws a RTException. A client calls this if 1) there is one or + * more error messages reported and 2) the client wishes to stop processing. + * Implementations of this method should only throw the RTException if there + * have been errors reported - if there are no errors, then this method does + * nothing. * * @throws RecorderException */ @@ -157,17 +176,16 @@ public interface MessageRecorder { /** * Add an Exception and extra informaton. * - * @param ex the Exception added. + * @param ex the Exception added. * @param info extra information (not meant to be part of printed message) * @throws RecorderException if too many error messages have been added. */ - void reportError(final Exception ex, final Object info) - throws RecorderException; + void reportError(final Exception ex, final Object info) throws RecorderException; /** * Add an error message. * - * @param msg the text of the error message. + * @param msg the text of the error message. * @throws RecorderException if too many error messages have been added. */ void reportError(final String msg) throws RecorderException; @@ -179,13 +197,12 @@ void reportError(final Exception ex, final Object info) * @param info extra information (not meant to be part of printed message) * @throws RecorderException if too many error messages have been added. */ - void reportError(final String msg, final Object info) - throws RecorderException; + void reportError(final String msg, final Object info) throws RecorderException; /** * Add a warning message. * - * @param msg the text of the warning message. + * @param msg the text of the warning message. */ void reportWarning(final String msg); @@ -200,12 +217,12 @@ void reportError(final String msg, final Object info) /** * Add an informational message. * - * @param msg the text of the info message. + * @param msg the text of the info message. */ void reportInfo(final String msg); /** - * Add an informational message and extra information. + * Add an informational message and extra information. * * @param msg the text of the info message. * @param info extra information (not meant to be part of printed message) diff --git a/mondrian/src/main/java/mondrian/recorder/RecorderException.java b/mondrian/src/main/java/mondrian/recorder/RecorderException.java index d680693430..3ad28e6250 100644 --- a/mondrian/src/main/java/mondrian/recorder/RecorderException.java +++ b/mondrian/src/main/java/mondrian/recorder/RecorderException.java @@ -1,26 +1,40 @@ /* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2005-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara and others -// All Rights Reserved. -*/ + * This software is subject to the terms of the Eclipse Public License v1.0 + * Agreement, available at the following URL: + * http://www.eclipse.org/legal/epl-v10.html. + * You must accept the terms of that agreement to use this software. + * + * Copyright (C) 2005-2005 Julian Hyde + * Copyright (c) 2002-2017 Hitachi Vantara.. All rights reserved. + * + * ---- All changes after Fork in 2023 ------------------------ + * + * Project: Eclipse daanse + * + * Copyright (c) 2023 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors after Fork in 2023: + * SmartCity Jena - initial + */ package mondrian.recorder; -import org.eclipse.daanse.olap.api.exception.OlapRuntimeException; - /** - * Exception thrown by {@link MessageRecorder} when too many errors - * have been reported. + * Exception thrown by {@link MessageRecorder} when too many errors have been + * reported. * * @author Richard M. Emberson */ -public final class RecorderException extends OlapRuntimeException { - protected RecorderException(String msg) { +public final class RecorderException extends RuntimeException { + private static final long serialVersionUID = 1L; + + protected RecorderException(String msg) { super(msg); } } diff --git a/mondrian/src/main/java/mondrian/recorder/package.html b/mondrian/src/main/java/mondrian/recorder/package.html deleted file mode 100644 index 6e38ce728d..0000000000 --- a/mondrian/src/main/java/mondrian/recorder/package.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - title - - -Provides a set a classes for logging the process of a task. - - - diff --git a/mondrian/src/main/java/mondrian/rolap/ArrayMemberSource.java b/mondrian/src/main/java/mondrian/rolap/ArrayMemberSource.java index a6d638da77..b3c1a4bdd6 100644 --- a/mondrian/src/main/java/mondrian/rolap/ArrayMemberSource.java +++ b/mondrian/src/main/java/mondrian/rolap/ArrayMemberSource.java @@ -16,12 +16,10 @@ import java.util.Collections; import java.util.List; -import mondrian.olap.exceptions.MdxCantFindMemberException; - import org.eclipse.daanse.olap.api.Segment; -import org.eclipse.daanse.olap.api.exception.OlapRuntimeException; import mondrian.olap.Util; +import mondrian.olap.exceptions.MdxCantFindMemberException; /** * ArrayMemberSource implements a flat, static hierarchy. There is * no root member, and all members are siblings. diff --git a/mondrian/src/main/java/mondrian/rolap/RolapCube.java b/mondrian/src/main/java/mondrian/rolap/RolapCube.java index 953e9ced14..e1d536a336 100644 --- a/mondrian/src/main/java/mondrian/rolap/RolapCube.java +++ b/mondrian/src/main/java/mondrian/rolap/RolapCube.java @@ -591,8 +591,8 @@ protected Query resolveCalcMembers( "RolapCube.resolveCalcMembers", new LocusImpl.Action() { @Override - public QueryImpl execute() { - final QueryImpl queryExp = + public Query execute() { + final Query queryExp = conn.parseQuery(queryString); queryExp.resolve(); return queryExp; diff --git a/mondrian/src/main/java/mondrian/rolap/RolapPhysicalCube.java b/mondrian/src/main/java/mondrian/rolap/RolapPhysicalCube.java index cff9e30742..4ddab174e1 100644 --- a/mondrian/src/main/java/mondrian/rolap/RolapPhysicalCube.java +++ b/mondrian/src/main/java/mondrian/rolap/RolapPhysicalCube.java @@ -34,7 +34,6 @@ import org.eclipse.daanse.olap.api.element.Property; import org.eclipse.daanse.olap.api.exception.OlapRuntimeException; import org.eclipse.daanse.olap.api.formatter.CellFormatter; -import org.eclipse.daanse.olap.api.query.component.Formula; import org.eclipse.daanse.rolap.aggregator.CountAggregator; import org.eclipse.daanse.rolap.element.RolapMetaData; import org.eclipse.daanse.rolap.mapping.api.model.ActionMapping; diff --git a/mondrian/src/main/java/mondrian/spi/DialectUtil.java b/mondrian/src/main/java/mondrian/spi/DialectUtil.java deleted file mode 100644 index 629e4414f1..0000000000 --- a/mondrian/src/main/java/mondrian/spi/DialectUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -/* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (c) 2015-2017 Hitachi Vantara. -// All rights reserved. - */ -package mondrian.spi; - -import java.util.regex.Pattern; - -public class DialectUtil { - - private static final Pattern UNICODE_CASE_FLAG_IN_JAVA_REG_EXP_PATTERN = Pattern.compile( "\\|\\(\\?u\\)" ); - private static final String EMPTY = ""; - - private DialectUtil() { - // constructor - } - - /** - * Cleans up the reqular expression from the unicode-aware case folding embedded flag expression (?u) - * - * @param javaRegExp - * the regular expression to clean up - * @return the cleaned regular expression - */ - public static String cleanUnicodeAwareCaseFlag( String javaRegExp ) { - String cleaned = javaRegExp; - if ( cleaned != null && isUnicodeCaseFlagInRegExp( cleaned ) ) { - cleaned = UNICODE_CASE_FLAG_IN_JAVA_REG_EXP_PATTERN.matcher( cleaned ).replaceAll( EMPTY ); - } - return cleaned; - } - - private static boolean isUnicodeCaseFlagInRegExp( String javaRegExp ) { - return UNICODE_CASE_FLAG_IN_JAVA_REG_EXP_PATTERN.matcher( javaRegExp ).find(); - } - -} - -//End DialectUtil.java diff --git a/mondrian/src/main/java/mondrian/spi/UserDefinedFunction.java b/mondrian/src/main/java/mondrian/spi/UserDefinedFunction.java deleted file mode 100644 index 6395abd69f..0000000000 --- a/mondrian/src/main/java/mondrian/spi/UserDefinedFunction.java +++ /dev/null @@ -1,148 +0,0 @@ -/* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2005-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara -// All Rights Reserved. -*/ - -package mondrian.spi; - -import java.util.List; - -import org.eclipse.daanse.olap.api.Evaluator; -import org.eclipse.daanse.olap.api.type.Type; - -/** - * Definition of a user-defined function. - * - *

The class must have a public, zero-arguments constructor, be on - * Mondrian's runtime class-path, and be referenced from the schema file: - * - *

- * <Schema>
- *     ....
- *     <UserDefinedFunction name="MyFun" - * class="com.acme.MyFun">
- * </Schema>
- * - * @author jhyde - */ -@Deprecated -public interface UserDefinedFunction { - /** - * Returns the name with which the user-defined function will be used - * from within MDX expressions. - */ - public String getName(); - - /** - * Returns a description of the user-defined function. - */ - public String getDescription(); - - - /** - * Returns an array of the types of the parameters of this function. - */ - public Type[] getParameterTypes(); - - /** - * Returns the return-type of this function. - * - * @param parameterTypes Parameter types - * @return Return type - */ - public Type getReturnType(Type[] parameterTypes); - - /** - * Applies this function to a set of arguments, and returns a result. - * - * @param evaluator Evaluator containts the runtime context, in particular - * the current member of each dimension. - * @param arguments Expressions which yield the arguments of this function. - * Most user-defined functions will evaluate all arguments before using - * them. Functions such as IIf do not evaluate all - * arguments; this technique is called lazy evaluation. - * @return The result value. - */ - public Object execute(Evaluator evaluator, Argument[] arguments); - - /** - * Returns a list of reserved words used by this function. - * May return an empty array or null if this function does not require - * any reserved words. - */ - default List getReservedWords(){ - return List.of(); - } - - interface Argument { - /** - * Returns the type of the argument. - * - * @return Argument type - */ - Type getType(); - - /** - * Evaluates the argument as a scalar expression. - * - *

The effect is the same as - * {@link #evaluate(org.eclipse.daanse.olap.api.Evaluator)} except if the argument - * evaluates to a member or tuple. This method will set the context - * to the member or tuple and evaluate the current measure, whereas - * {@code evaluate} would return the member or tuple. - * - *

The effect is similar to creating a calculated member in an MDX - * query:

- * - *
WITH MEMBER [Measures].[Previous Period] AS
- *   ([Measures].[Unit Sales], [Time].[Time].PrevMember)
- * SELECT {[Measures].[Unit Sales],
- *     [Measures].[Previous Period]} on 0,
- *   [Time].[Time].Children on 1
- * FROM [Sales]
- * - *

Note how {@code [Measures].[Previous Period]} is defined as a - * tuple, but evaluates to a number.

- * - * @param evaluator Evaluation context - * @return Scalar expression at the given member or tuple - */ - Object evaluateScalar(Evaluator evaluator); - - /** - * Evaluates the argument. - * - *

If the argument is a set of members or tuples, this method may - * return either a {@link List} or an {@link Iterable}. It is not safe - * to blindly cast to {@code List}. For guaranteed type, call - * {@link #evaluateList(org.eclipse.daanse.olap.api.Evaluator)} or - * {@link #evaluateIterable(org.eclipse.daanse.olap.api.Evaluator)}. - * - * @param evaluator Evaluation context - * @return Result of evaluating the argument - */ - Object evaluate(Evaluator evaluator); - - /** - * Evaluates the argument to a list of members or tuples. - * - * @param eval Evaluation context - * @return List of members or tuples. - */ - List evaluateList(Evaluator eval); - - /** - * Evaluates the argument to an iterable over members or tuples. - * - * @param eval Evaluation context - * @return Iterable over members or tuples. - */ - Iterable evaluateIterable(Evaluator eval); - } -} diff --git a/mondrian/src/main/java/mondrian/util/ServiceDiscovery.java b/mondrian/src/main/java/mondrian/util/ServiceDiscovery.java index 7262930b5d..98ec43d9d6 100644 --- a/mondrian/src/main/java/mondrian/util/ServiceDiscovery.java +++ b/mondrian/src/main/java/mondrian/util/ServiceDiscovery.java @@ -19,7 +19,6 @@ import org.slf4j.LoggerFactory; import aQute.bnd.annotation.spi.ServiceConsumer; -import mondrian.spi.UserDefinedFunction; /** * Utility functions to discover Java services. diff --git a/mondrian/src/main/java/org/eclipse/daanse/olap/impl/ScenarioImpl.java b/mondrian/src/main/java/org/eclipse/daanse/olap/impl/ScenarioImpl.java index 157184ba2b..ec8ec4bb72 100644 --- a/mondrian/src/main/java/org/eclipse/daanse/olap/impl/ScenarioImpl.java +++ b/mondrian/src/main/java/org/eclipse/daanse/olap/impl/ScenarioImpl.java @@ -11,13 +11,13 @@ import org.eclipse.daanse.olap.api.element.Hierarchy; import org.eclipse.daanse.olap.api.element.Member; import org.eclipse.daanse.olap.api.query.component.Formula; +import org.eclipse.daanse.olap.api.query.component.Query; import org.eclipse.daanse.olap.api.result.AllocationPolicy; import org.eclipse.daanse.olap.api.result.Result; import org.eclipse.daanse.olap.api.result.Scenario; import org.eclipse.daanse.olap.api.result.WritebackCell; import org.eclipse.daanse.olap.api.type.ScalarType; import org.eclipse.daanse.olap.calc.base.nested.AbstractProfilingNestedUnknownCalc; -import org.eclipse.daanse.olap.query.component.QueryImpl; import org.eclipse.daanse.olap.query.component.ResolvedFunCallImpl; import mondrian.olap.Util; @@ -321,7 +321,7 @@ private static double computeAtomicCellCount( final String mdx = buf.toString(); final RolapConnection connection = cube.getCatalog().getInternalConnection(); - final QueryImpl query = connection.parseQuery(mdx); + final Query query = connection.parseQuery(mdx); final Result result = connection.execute(query); final Object o = result.getCell(new int[0]).getValue(); return o instanceof Number diff --git a/mondrian/src/test/java/mondrian/olap/IdBatchResolverTest.java b/mondrian/src/test/java/mondrian/olap/IdBatchResolverTest.java index c5a8f172d5..25bf9dc799 100644 --- a/mondrian/src/test/java/mondrian/olap/IdBatchResolverTest.java +++ b/mondrian/src/test/java/mondrian/olap/IdBatchResolverTest.java @@ -67,7 +67,7 @@ class IdBatchResolverTest { @Mock - QueryImpl query; + Query query; @Captor ArgumentCaptor> childNames; diff --git a/mondrian/src/test/java/mondrian/rolap/SchemaModifiers.java b/mondrian/src/test/java/mondrian/rolap/SchemaModifiers.java index 29d3337583..682d2f6217 100644 --- a/mondrian/src/test/java/mondrian/rolap/SchemaModifiers.java +++ b/mondrian/src/test/java/mondrian/rolap/SchemaModifiers.java @@ -38,8 +38,8 @@ import org.eclipse.daanse.rolap.mapping.api.model.enums.AccessHierarchy; import org.eclipse.daanse.rolap.mapping.api.model.enums.AccessMember; import org.eclipse.daanse.rolap.mapping.api.model.enums.ColumnDataType; -import org.eclipse.daanse.rolap.mapping.api.model.enums.InternalDataType; import org.eclipse.daanse.rolap.mapping.api.model.enums.HideMemberIfType; +import org.eclipse.daanse.rolap.mapping.api.model.enums.InternalDataType; import org.eclipse.daanse.rolap.mapping.api.model.enums.LevelType; import org.eclipse.daanse.rolap.mapping.api.model.enums.RollupPolicyType; import org.eclipse.daanse.rolap.mapping.instance.rec.complex.foodmart.FoodmartMappingSupplier; @@ -64,7 +64,6 @@ import org.eclipse.daanse.rolap.mapping.pojo.CatalogMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.CellFormatterMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.CountMeasureMappingImpl; -import org.eclipse.daanse.rolap.mapping.pojo.PhysicalColumnMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.CubeConnectorMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.CubeMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.DatabaseSchemaMappingImpl; @@ -79,11 +78,11 @@ import org.eclipse.daanse.rolap.mapping.pojo.MaxMeasureMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.MeasureGroupMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.MeasureMappingImpl; -import org.eclipse.daanse.rolap.mapping.pojo.SumMeasureMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.MemberFormatterMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.MemberPropertyFormatterMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.MemberPropertyMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.ParentChildLinkMappingImpl; +import org.eclipse.daanse.rolap.mapping.pojo.PhysicalColumnMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.PhysicalCubeMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.PhysicalTableMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.RowMappingImpl; @@ -93,11 +92,14 @@ import org.eclipse.daanse.rolap.mapping.pojo.SqlStatementMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.SqlViewMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.StandardDimensionMappingImpl; +import org.eclipse.daanse.rolap.mapping.pojo.SumMeasureMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.TableQueryMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.TimeDimensionMappingImpl; import org.eclipse.daanse.rolap.mapping.pojo.VirtualCubeMappingImpl; -import mondrian.test.UdfTest; +import mondrian.test.FormatterUtil; + + public class SchemaModifiers { @@ -7229,7 +7231,7 @@ protected List cubeCalculatedMembers(CubeMapp .build(), CalculatedMemberPropertyMappingImpl.builder() .withName("CELL_FORMATTER") - .withValue(UdfTest.FooBarCellFormatter.class.getName()) + .withValue(FormatterUtil.FooBarCellFormatter.class.getName()) .build() )) .build()); @@ -8138,7 +8140,7 @@ protected List physicalCubeMeasureGroups(Physical .withName("Unit Sales Foo Bar") .withColumn(FoodmartMappingSupplier.UNIT_SALES_COLUMN_IN_SALES_FACT_1997) - .withFormatter(UdfTest.FooBarCellFormatter.class.getName()) + .withFormatter(FormatterUtil.FooBarCellFormatter.class.getName()) .withMeasureGroup(mg) .build())); } @@ -9426,7 +9428,7 @@ protected List physicalCubeMeasureGroups(Physical .withColumn(FoodmartMappingSupplier.UNIT_SALES_COLUMN_IN_SALES_FACT_1997) .withFormatString("Standard") - .withFormatter(UdfTest.FooBarCellFormatter.class.getName()) + .withFormatter(FormatterUtil.FooBarCellFormatter.class.getName()) .build())); } return result; @@ -9501,7 +9503,7 @@ protected List cubeCalculatedMembers(CubeMapp .withCalculatedMemberProperties(List.of( CalculatedMemberPropertyMappingImpl.builder() .withName("CELL_FORMATTER") - .withValue(UdfTest.FooBarCellFormatter.class.getName()) + .withValue(FormatterUtil.FooBarCellFormatter.class.getName()) .build() )) .build()); @@ -9535,7 +9537,7 @@ protected List cubeCalculatedMembers(CubeMapp .withFormula("[Measures].[Unit Sales]") //.dimension("Measures") .withCellFormatter(CellFormatterMappingImpl.builder() - .withRef(UdfTest.FooBarCellFormatter.class.getName()) + .withRef(FormatterUtil.FooBarCellFormatter.class.getName()) .build()) .build()); } @@ -9622,7 +9624,7 @@ protected List cubeDimensionConnectors(Cube .withUniqueMembers(true) //.withFormatter(UdfTest.FooBarMemberFormatter.class.getName()) .withMemberFormatter(MemberFormatterMappingImpl.builder() - .withRef(UdfTest.FooBarMemberFormatter.class.getName()) + .withRef(FormatterUtil.FooBarMemberFormatter.class.getName()) .build()) .build() )) @@ -9746,7 +9748,7 @@ protected List cubeDimensionConnectors(Cube MemberPropertyMappingImpl.builder() .withName("Medium") .withColumn(FoodmartMappingSupplier.MEDIA_TYPE_COLUMN_IN_PROMOTION) - .withFormatter(MemberPropertyFormatterMappingImpl.builder().withRef(UdfTest.FooBarPropertyFormatter.class.getName()).build()) + .withFormatter(MemberPropertyFormatterMappingImpl.builder().withRef(FormatterUtil.FooBarPropertyFormatter.class.getName()).build()) .build() )) .build() @@ -9811,7 +9813,7 @@ protected List cubeDimensionConnectors(Cube MemberPropertyMappingImpl.builder() .withName("Medium") .withColumn(FoodmartMappingSupplier.MEDIA_TYPE_COLUMN_IN_PROMOTION) - .withFormatter(MemberPropertyFormatterMappingImpl.builder().withRef(UdfTest.FooBarPropertyFormatter.class.getName()).build()) + .withFormatter(MemberPropertyFormatterMappingImpl.builder().withRef(FormatterUtil.FooBarPropertyFormatter.class.getName()).build()) .build() )) .build() @@ -10045,34 +10047,34 @@ protected List schemaUserDefinedFunctions(MappingSch */ } - public static class UdfTestModifier17 extends PojoMappingModifier { - - /* - "\n"); - */ - - private final Class functionClass; - - public UdfTestModifier17(CatalogMapping catalog, final Class functionClass) { - super(catalog); - this.functionClass = functionClass; - } - - /* TODO: UserDefinedFunction - @Override - protected List schemaUserDefinedFunctions(MappingSchema schema) { - List result = new ArrayList<>(); - result.addAll(super.schemaUserDefinedFunctions(schema).stream().filter(f -> !"Reverse".equals(f.name())).toList()); - result.add(UserDefinedFunctionRBuilder.builder() - .name("Reverse") - .className(functionClass.getName()) - .build()); - return result; - } - */ - } +// public static class UdfTestModifier17 extends PojoMappingModifier { +// +// /* +// "\n"); +// */ +// +// private final Class functionClass; +// +// public UdfTestModifier17(CatalogMapping catalog, final Class functionClass) { +// super(catalog); +// this.functionClass = functionClass; +// } +// +// /* TODO: UserDefinedFunction +// @Override +// protected List schemaUserDefinedFunctions(MappingSchema schema) { +// List result = new ArrayList<>(); +// result.addAll(super.schemaUserDefinedFunctions(schema).stream().filter(f -> !"Reverse".equals(f.name())).toList()); +// result.add(UserDefinedFunctionRBuilder.builder() +// .name("Reverse") +// .className(functionClass.getName()) +// .build()); +// return result; +// } +// */ +// } public static class UdfTestModifier18 extends PojoMappingModifier { diff --git a/mondrian/src/test/java/mondrian/rolap/aggmatcher/AggGenTest.java b/mondrian/src/test/java/mondrian/rolap/aggmatcher/AggGenTest.java index 3107272da7..b66a91d99e 100644 --- a/mondrian/src/test/java/mondrian/rolap/aggmatcher/AggGenTest.java +++ b/mondrian/src/test/java/mondrian/rolap/aggmatcher/AggGenTest.java @@ -23,6 +23,7 @@ import javax.sql.DataSource; import org.eclipse.daanse.olap.api.Context; +import org.eclipse.daanse.olap.api.query.component.Query; import org.eclipse.daanse.olap.query.component.QueryImpl; import org.eclipse.daanse.olap.rolap.api.RolapContext; import org.eclipse.daanse.rolap.mapping.api.model.CatalogMapping; @@ -72,7 +73,7 @@ void testCallingLoadColumnsInAddCollapsedColumnOrAddzSpecialCollapsedColumn(Cont ((TestContextImpl)context).setGenerateAggregateSql(true); final RolapConnection rolapConn = (RolapConnection) context.getConnectionWithDefaultRole(); - QueryImpl query = + Query query = rolapConn.parseQuery( "select {[Measures].[Count]} on columns from [HR]"); rolapConn.execute(query); diff --git a/mondrian/src/test/java/mondrian/rolap/aggmatcher/DefaultRuleTest.java b/mondrian/src/test/java/mondrian/rolap/aggmatcher/DefaultRuleTest.java index ee73221aa5..2cde468069 100644 --- a/mondrian/src/test/java/mondrian/rolap/aggmatcher/DefaultRuleTest.java +++ b/mondrian/src/test/java/mondrian/rolap/aggmatcher/DefaultRuleTest.java @@ -60,8 +60,8 @@ public static void beforeAll() throws Exception { LOGGER.error("HAS ERRORS"); for (Iterator it = msgRecorder.getErrorEntries(); it.hasNext();) { ListRecorder.Entry e = (ListRecorder.Entry) it.next(); - LOGGER.error("context=" + e.getContext()); - LOGGER.error("message=" + e.getMessage()); + LOGGER.error("context=" + e.context()); + LOGGER.error("message=" + e.message()); } } } diff --git a/mondrian/src/test/java/mondrian/spi/DialectUtilTest.java b/mondrian/src/test/java/mondrian/spi/DialectUtilTest.java deleted file mode 100644 index 2ded50611e..0000000000 --- a/mondrian/src/test/java/mondrian/spi/DialectUtilTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (c) 2015-2017 Hitachi Vantara. -// All rights reserved. - */ -package mondrian.spi; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import org.junit.jupiter.api.Test; - -class DialectUtilTest{ - - @Test - void testCleanUnicodeAwareCaseFlag_InputNull() { - String inputExpression = null; - String cleaned = DialectUtil.cleanUnicodeAwareCaseFlag( inputExpression ); - assertNull( cleaned ); - } - - @Test - void testCleanUnicodeAwareCaseFlag_InputContainsFlag() { - String inputExpression = "(?i)|(?u).*a.*"; - String expectedExpression = "(?i).*a.*"; - String cleaned = DialectUtil.cleanUnicodeAwareCaseFlag( inputExpression ); - assertEquals( expectedExpression, cleaned ); - } - - @Test - void testCleanUnicodeAwareCaseFlag_InputNotContainsFlag() { - String inputExpression = "(?i).*a.*"; - String expectedExpression = "(?i).*a.*"; - String cleaned = DialectUtil.cleanUnicodeAwareCaseFlag( inputExpression ); - assertEquals( expectedExpression, cleaned ); - } - -} -//End DialectUtilTest.java diff --git a/mondrian/src/test/java/mondrian/test/BasicQueryTest.java b/mondrian/src/test/java/mondrian/test/BasicQueryTest.java index 0cad43f3fa..e2aba91930 100644 --- a/mondrian/src/test/java/mondrian/test/BasicQueryTest.java +++ b/mondrian/src/test/java/mondrian/test/BasicQueryTest.java @@ -106,7 +106,6 @@ import mondrian.rolap.SchemaModifiers; import mondrian.server.ExecutionImpl; import mondrian.spi.StatisticsProvider; -import mondrian.spi.UserDefinedFunction; import mondrian.spi.impl.JdbcStatisticsProvider; import mondrian.spi.impl.SqlStatisticsProvider; import mondrian.spi.impl.SqlStatisticsProviderNew; @@ -5251,102 +5250,6 @@ void testMondrian1432_ZeroAxisSegment(Context context) { + "{[Gender].[Gender].[All Gender]}\n" + "Row #0: 0\n" ); } - /** - * A simple user-defined function which adds one to its argument, but sleeps 1 ms before doing so. - */ - public static class SleepUdf implements UserDefinedFunction { - @Override - public String getName() { - return "SleepUdf"; - } - - @Override - public String getDescription() { - return "Returns its argument plus one but sleeps 1 ms first"; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - - @Override - public Type getReturnType( Type[] parameterTypes ) { - return NumericType.INSTANCE; - } - - @Override - public Type[] getParameterTypes() { - return new Type[] { NumericType.INSTANCE }; - } - - @Override - public Object execute( Evaluator evaluator, Argument[] arguments ) { - final Object argValue = arguments[0].evaluateScalar( evaluator ); - if ( argValue instanceof Number ) { - try { - Thread.sleep( 1 ); - } catch ( Exception ex ) { - return null; - } - return ( (Number) argValue ).doubleValue() + 1; - } else { - // Argument might be a RuntimeException indicating that - // the cache does not yet have the required cell value. The - // function will be called again when the cache is loaded. - return null; - } - } - - } - - public static class CountConcurrentUdf implements UserDefinedFunction { - private static AtomicInteger count = new AtomicInteger(); - - @Override - public String getName() { - return "CountConcurrentUdf"; - } - - @Override - public String getDescription() { - return "Counts the current number of threads using this thing."; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - - @Override - public Type getReturnType( Type[] parameterTypes ) { - return NumericType.INSTANCE; - } - - @Override - public Type[] getParameterTypes() { - return new Type[] {}; - } - - @Override - public Object execute( Evaluator evaluator, Argument[] arguments ) { - try { - count.incrementAndGet(); - Thread.sleep( 10000 ); - } catch ( InterruptedException ex ) { - Thread.currentThread().interrupt(); - } finally { - count.decrementAndGet(); - } - throw new Error( "Leaving." ); - } - - static int getCount() { - return count.get(); - } - - } - /** * This unit test would cause connection leaks without a fix for bug * MONDRIAN-571, @@ -5561,67 +5464,6 @@ void testResultLimitWithinLimit(Context context) { "select CrossJoin([Product].[Brand Name].Members, [Gender].[Gender].Members) on columns from [Sales]" ); } - /** - * This is a test for MONDRIAN-1161. It verifies that two - * queries can run at the same time. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class ) - void testConcurrentStatementRun_2(Context context) throws Exception { - // timeout is issued after 2 seconds so the test query needs to - // run for at least that long; it will because the query references - // a Udf that has a 1 ms sleep in it; and there are enough rows - // in the result that the Udf should execute > 2000 times - /* - String baseSchema = TestUtil.getRawSchema(context); - String schema = SchemaUtil.getSchema(baseSchema, null, null, null, null, - "", - null ); - */ - TestUtil.withSchema(context, SchemaModifiers.BasicQueryTestModifier29::new); - final String query = - "WITH\n" + " MEMBER [Measures].[CountyThigny]\n" + " AS 'CountConcurrentUdf()'\n" - + "SELECT {[Measures].[CountyThigny]} ON COLUMNS,\n" + " {[Product].members} ON ROWS\n" + "FROM [Sales]"; - - final ExecutorService es = Executors.newCachedThreadPool( new ThreadFactory() { - @Override - public Thread newThread( Runnable r ) { - final Thread thread = Executors.defaultThreadFactory().newThread( r ); - thread.setName( "mondrian.test.BasicQueryTest.testConcurrentStatementRun_2" ); - thread.setDaemon( true ); - return thread; - } - } ); - - // Submit a query twice. - Future task1 = es.submit( new Callable() { - @Override - public Result call() throws Exception { - return executeQuery(context.getConnectionWithDefaultRole(), query ); - } - } ); - Future task2 = es.submit( new Callable() { - @Override - public Result call() throws Exception { - return executeQuery(context.getConnectionWithDefaultRole(), query ); - } - } ); - - // Let the backend run a bit - Thread.sleep( 5000 ); - - // There should be 2 queries running. - try { - assertEquals( 2, CountConcurrentUdf.getCount() ); - } finally { - // cleanup and leave. - task1.cancel( true ); - task2.cancel( true ); - es.shutdownNow(); - } - } - /** * Test for MONDRIAN-1560 Verifies that various references to a member resolve correctly when case.sensitive=false */ diff --git a/mondrian/src/test/java/mondrian/test/FormatterUtil.java b/mondrian/src/test/java/mondrian/test/FormatterUtil.java new file mode 100644 index 0000000000..dec754268f --- /dev/null +++ b/mondrian/src/test/java/mondrian/test/FormatterUtil.java @@ -0,0 +1,58 @@ +/* +// This software is subject to the terms of the Eclipse Public License v1.0 +// Agreement, available at the following URL: +// http://www.eclipse.org/legal/epl-v10.html. +// You must accept the terms of that agreement to use this software. +// +// Copyright (C) 2005-2005 Julian Hyde +// Copyright (C) 2005-2017 Hitachi Vantara +// All Rights Reserved. +*/ + +package mondrian.test; + +import org.eclipse.daanse.olap.api.element.Member; +import org.eclipse.daanse.olap.api.element.Property; +import org.eclipse.daanse.olap.api.formatter.CellFormatter; +import org.eclipse.daanse.olap.api.formatter.MemberFormatter; +import org.eclipse.daanse.olap.api.formatter.MemberPropertyFormatter; + + +public class FormatterUtil { + + /** + * Member formatter for test purposes. Returns name of the member prefixed + * with "foo" and suffixed with "bar". + */ + public static class FooBarMemberFormatter implements MemberFormatter { + @Override + public String format(Member member) { + return "foo" + member.getName() + "bar"; + } + } + + /** + * Cell formatter for test purposes. Returns value of the cell prefixed + * with "foo" and suffixed with "bar". + */ + public static class FooBarCellFormatter implements CellFormatter { + @Override + public String format(Object value) { + return "foo" + value + "bar"; + } + } + + /** + * Property formatter for test purposes. Returns name of the member and + * property, then the value, prefixed with "foo" and suffixed with "bar". + */ + public static class FooBarPropertyFormatter implements MemberPropertyFormatter { + @Override + public String format( + Member member, Property property, Object propertyValue) + { + return "foo" + member.getName() + "/" + property.getName() + "/" + + propertyValue + "bar"; + } + } +} diff --git a/mondrian/src/test/java/mondrian/test/PerformanceTest.java b/mondrian/src/test/java/mondrian/test/PerformanceTest.java index fc15fd4856..075096faac 100644 --- a/mondrian/src/test/java/mondrian/test/PerformanceTest.java +++ b/mondrian/src/test/java/mondrian/test/PerformanceTest.java @@ -49,7 +49,6 @@ import mondrian.olap.SystemWideProperties; import mondrian.olap.fun.sort.Sorter; import mondrian.rolap.SchemaModifiers; -import mondrian.spi.UserDefinedFunction; import mondrian.util.Bug; /** @@ -597,65 +596,7 @@ private static long printDuration( String desc, long t0 ) { return duration; } - /** - * Test for - * MONDRIAN-1242, - * "Slicer size is exponentially inflating the cell requests". This case just checks correctness; a similar case - * in {@link PerformanceTest} checks performance. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testBugMondrian1242(Context context) { - withSchema(context, SchemaModifiers.PerformanceTestModifier4::new); - // original test case for MONDRIAN-1242; ensures correct result - Connection connection = context.getConnectionWithDefaultRole(); - assertQueryReturns(connection, - "select {[Measures].[Unit Sales]} on COLUMNS,\n" - + "[Store].[USA].[CA].Children on ROWS\n" - + "from [Sales]\n" - + "where ([Time].[Weekly].[All Time].[Weeklys].[1997].[4].[16]\n" - + " : [Time].[Weekly].[All Time].[Weeklys].[1997].[5].[25])", - "Axis #0:\n" - + "{[Time].[Weekly].[1997].[4].[16]}\n" - + "{[Time].[Weekly].[1997].[4].[17]}\n" - + "{[Time].[Weekly].[1997].[4].[18]}\n" - + "{[Time].[Weekly].[1997].[5].[19]}\n" - + "{[Time].[Weekly].[1997].[5].[20]}\n" - + "{[Time].[Weekly].[1997].[5].[21]}\n" - + "{[Time].[Weekly].[1997].[5].[22]}\n" - + "{[Time].[Weekly].[1997].[5].[23]}\n" - + "{[Time].[Weekly].[1997].[5].[24]}\n" - + "{[Time].[Weekly].[1997].[5].[25]}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "Axis #2:\n" - + "{[Store].[USA].[CA].[Alameda]}\n" - + "{[Store].[USA].[CA].[Beverly Hills]}\n" - + "{[Store].[USA].[CA].[Los Angeles]}\n" - + "{[Store].[USA].[CA].[San Diego]}\n" - + "{[Store].[USA].[CA].[San Francisco]}\n" - + "Row #0: \n" - + "Row #1: 250\n" - + "Row #2: 724\n" - + "Row #3: 451\n" - + "Row #4: 18\n" ); - CounterUdf.count.set( 0 ); - Result result = executeQuery(connection, - "with member [Measures].[Foo] as CounterUdf()\n" - + "select {[Measures].[Foo]} on COLUMNS,\n" - + "[Gender].Children on ROWS\n" - + "from [Sales]\n" - + "where ([Customers].[USA].[CA].[Altadena].[Alice Cantrell]" - + " : [Customers].[USA].[CA].[Altadena].[Alice Cantrell].Lead(7000))" ); - assertEquals( - "111,191", - result.getCell( new int[] { 0, 0 } ).getFormattedValue() ); - // Count is 4 if bug is fixed, - // 28004 if bug is not fixed. - assertEquals( 4, CounterUdf.count.get() ); - } /** * Tests performance of {@link Sorter#stablePartialSort}. @@ -855,44 +796,6 @@ private void printDurations() { } } - /** - * User-defined function that counts how many times it has been invoked. - */ - public static class CounterUdf implements UserDefinedFunction { - public static final AtomicInteger count = new AtomicInteger(); - - @Override - public String getName() { - return "CounterUdf"; - } - - @Override - public String getDescription() { - return "Counts the number of times it is called."; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - - @Override - public Type getReturnType( Type[] parameterTypes ) { - return NumericType.INSTANCE; - } - - @Override - public Type[] getParameterTypes() { - return new Type[] {}; - } - - @Override - public Object execute( Evaluator evaluator, Argument[] arguments ) { - count.incrementAndGet(); - return evaluator.evaluateCurrent(); - } - - } private static class CountingComparator> implements Comparator { diff --git a/mondrian/src/test/java/mondrian/test/TestCalculatedMembers.java b/mondrian/src/test/java/mondrian/test/TestCalculatedMembers.java index 96426433a6..7de19b3244 100644 --- a/mondrian/src/test/java/mondrian/test/TestCalculatedMembers.java +++ b/mondrian/src/test/java/mondrian/test/TestCalculatedMembers.java @@ -897,7 +897,7 @@ void testCalcMemberCustomFormatterInQuery(Context context) { assertQueryReturns(context.getConnectionWithDefaultRole(), "with member [Measures].[Foo] as ' [Measures].[Unit Sales] * 2 ',\n" + " CELL_FORMATTER='" - + UdfTest.FooBarCellFormatter.class.getName() + + FormatterUtil.FooBarCellFormatter.class.getName() + "' \n" + "select {[Measures].[Unit Sales], [Measures].[Foo]} on 0,\n" + " {[Store].Children} on rows\n" diff --git a/mondrian/src/test/java/mondrian/test/UdfTest.java b/mondrian/src/test/java/mondrian/test/UdfTest.java deleted file mode 100644 index 50315b1db0..0000000000 --- a/mondrian/src/test/java/mondrian/test/UdfTest.java +++ /dev/null @@ -1,2001 +0,0 @@ -/* -// This software is subject to the terms of the Eclipse Public License v1.0 -// Agreement, available at the following URL: -// http://www.eclipse.org/legal/epl-v10.html. -// You must accept the terms of that agreement to use this software. -// -// Copyright (C) 2005-2005 Julian Hyde -// Copyright (C) 2005-2017 Hitachi Vantara -// All Rights Reserved. -*/ - -package mondrian.test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.opencube.junit5.TestUtil.assertAxisReturns; -import static org.opencube.junit5.TestUtil.assertExprReturns; -import static org.opencube.junit5.TestUtil.assertMatchesVerbose; -import static org.opencube.junit5.TestUtil.assertQueryReturns; -import static org.opencube.junit5.TestUtil.assertQueryThrows; -import static org.opencube.junit5.TestUtil.executeExpr; -import static org.opencube.junit5.TestUtil.executeExprRaw; -import static org.opencube.junit5.TestUtil.executeOlap4jQuery; -import static org.opencube.junit5.TestUtil.executeQuery; -import static org.opencube.junit5.TestUtil.withSchema; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.function.Function; -import java.util.regex.Pattern; - -import org.eclipse.daanse.olap.api.Connection; -import org.eclipse.daanse.olap.api.Context; -import org.eclipse.daanse.olap.api.Evaluator; -import org.eclipse.daanse.olap.api.Statement; -import org.eclipse.daanse.olap.api.element.Hierarchy; -import org.eclipse.daanse.olap.api.element.Member; -import org.eclipse.daanse.olap.api.element.Property; -import org.eclipse.daanse.olap.api.formatter.CellFormatter; -import org.eclipse.daanse.olap.api.formatter.MemberFormatter; -import org.eclipse.daanse.olap.api.formatter.MemberPropertyFormatter; -import org.eclipse.daanse.olap.api.result.Axis; -import org.eclipse.daanse.olap.api.result.Cell; -import org.eclipse.daanse.olap.api.result.CellSet; -import org.eclipse.daanse.olap.api.result.Result; -import org.eclipse.daanse.olap.api.type.HierarchyType; -import org.eclipse.daanse.olap.api.type.MemberType; -import org.eclipse.daanse.olap.api.type.NumericType; -import org.eclipse.daanse.olap.api.type.SetType; -import org.eclipse.daanse.olap.api.type.StringType; -import org.eclipse.daanse.olap.api.type.Type; -import org.eclipse.daanse.olap.impl.StatementImpl; -import org.eclipse.daanse.olap.rolap.api.RolapContext; -import org.eclipse.daanse.rolap.mapping.api.model.CatalogMapping; -import org.eclipse.daanse.rolap.mapping.modifier.pojo.PojoMappingModifier; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; -import org.junit.jupiter.params.ParameterizedTest; -import org.opencube.junit5.ContextSource; -import org.opencube.junit5.TestUtil; -import org.opencube.junit5.context.TestContext; -import org.opencube.junit5.dataloader.FastFoodmardDataLoader; -import org.opencube.junit5.propupdator.AppandFoodMartCatalog; - -import mondrian.olap.Util; -import mondrian.rolap.SchemaModifiers; -import mondrian.spi.UserDefinedFunction; - -/** - * Unit-test for {@link UserDefinedFunction user-defined functions}. - * Also tests {@link CellFormatter cell formatters} - * and {@link MemberFormatter member formatters}. - * - *

TODO: - * 1. test that function which does not return a name, description etc. - * gets a sensible error - * 2. document UDFs - * - * @author jhyde - * @since Apr 29, 2005 - */ -public class UdfTest { - - - - private void prepareContext(Context context) { - updateTestContext(context, SchemaModifiers.UdfTestModifier11::new); - } - - /** - * Shorthand for containing a test context that consists of the standard - * FoodMart schema plus a UDF. - * - * @param xmlUdf UDF definition - * @return Test context - */ - /* - private void udfTestContext(Context context, String xmlUdf) { - String baseSchema = TestUtil.getRawSchema(context); - String schema = SchemaUtil.getSchema(baseSchema, - null, null, null, null, xmlUdf, null); - TestUtil.withSchema(context, schema); - } - */ - - /** - * Shorthand for containing a test context that consists of the standard - * FoodMart Sales cube plus one measure. - * - * @param xmlMeasure Measure definition - * @return Test context - */ - private void updateTestContext(Context context, Function f) { - withSchema(context, f); - } - - private void updateTestContext(Context context, PojoMappingModifier m) { - context.getCatalogCache().clear(); - ((TestContext)context).setCatalogMappingSupplier(m); - } - - - // ~ Tests follow ---------------------------------------------------------- - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testSanity(Context context) { - // sanity check, make sure the schema is loading correctly - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {[Measures].[Store Sqft]} ON COLUMNS, {[Store Type]} ON ROWS FROM [Store]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Store Sqft]}\n" - + "Axis #2:\n" - + "{[Store Type].[Store Type].[All Store Types]}\n" - + "Row #0: 571,596\n"); - } - - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testFun(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "WITH MEMBER [Measures].[Sqft Plus One] AS 'PlusOne([Measures].[Store Sqft])'\n" - + "SELECT {[Measures].[Sqft Plus One]} ON COLUMNS, \n" - + " {[Store Type].children} ON ROWS \n" - + "FROM [Store]", - - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Sqft Plus One]}\n" - + "Axis #2:\n" - + "{[Store Type].[Deluxe Supermarket]}\n" - + "{[Store Type].[Gourmet Supermarket]}\n" - + "{[Store Type].[HeadQuarters]}\n" - + "{[Store Type].[Mid-Size Grocery]}\n" - + "{[Store Type].[Small Grocery]}\n" - + "{[Store Type].[Supermarket]}\n" - + "Row #0: 146,046\n" - + "Row #1: 47,448\n" - + "Row #2: \n" - + "Row #3: 109,344\n" - + "Row #4: 75,282\n" - + "Row #5: 193,481\n"); - } - - /** - * Test case for bug - * MONDRIAN-1200, - * "User-defined function + profiling causes NPE in CalcWriter". - * The bug only occurs if manually enable "mondrian.profile" logger before - * running this test. (The bug requires olap4j, plus profiling, plus a - * query that calls a UDF with one or more arguments on an axis.) - * - * @throws SQLException on error - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testFunWithProfiling(Context context) throws SQLException { - prepareContext(context); - Connection connection = null; - Statement statement = null; - CellSet x = null; - try { - connection = context.getConnectionWithDefaultRole(); - statement = connection.createStatement(); - x = statement.executeQuery( - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[yyyy]\\.[Qq]\\.[m]\", BEFORE)} " - + "ON COLUMNS FROM [Sales]"); - TestUtil.toString(x); - } finally { - Util.close(x, ((StatementImpl) statement), connection); - } - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testLastNonEmpty(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "WITH MEMBER [Measures].[Last Unit Sales] AS \n" - + " '([Measures].[Unit Sales], \n" - + " LastNonEmpty(Descendants([Time].[Time]), [Measures].[Unit Sales]))'\n" - + "SELECT {[Measures].[Last Unit Sales]} ON COLUMNS,\n" - + " CrossJoin(\n" - + " {[Time].[1997], [Time].[1997].[Q1], [Time].[1997].[Q1].Children},\n" - + " {[Product].[All Products].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].children}) ON ROWS\n" - + "FROM [Sales]\n" - + "WHERE ([Store].[All Stores].[USA].[OR].[Portland].[Store 11])", - "Axis #0:\n" - + "{[Store].[Store].[USA].[OR].[Portland].[Store 11]}\n" - + "Axis #1:\n" - + "{[Measures].[Last Unit Sales]}\n" - + "Axis #2:\n" - + "{[Time].[Time].[1997], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Good]}\n" - + "{[Time].[Time].[1997], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Pearl]}\n" - + "{[Time].[Time].[1997], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Portsmouth]}\n" - + "{[Time].[Time].[1997], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Top Measure]}\n" - + "{[Time].[Time].[1997], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Walrus]}\n" - + "{[Time].[Time].[1997].[Q1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Good]}\n" - + "{[Time].[Time].[1997].[Q1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Pearl]}\n" - + "{[Time].[Time].[1997].[Q1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Portsmouth]}\n" - + "{[Time].[Time].[1997].[Q1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Top Measure]}\n" - + "{[Time].[Time].[1997].[Q1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Walrus]}\n" - + "{[Time].[Time].[1997].[Q1].[1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Good]}\n" - + "{[Time].[Time].[1997].[Q1].[1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Pearl]}\n" - + "{[Time].[Time].[1997].[Q1].[1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Portsmouth]}\n" - + "{[Time].[Time].[1997].[Q1].[1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Top Measure]}\n" - + "{[Time].[Time].[1997].[Q1].[1], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Walrus]}\n" - + "{[Time].[Time].[1997].[Q1].[2], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Good]}\n" - + "{[Time].[Time].[1997].[Q1].[2], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Pearl]}\n" - + "{[Time].[Time].[1997].[Q1].[2], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Portsmouth]}\n" - + "{[Time].[Time].[1997].[Q1].[2], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Top Measure]}\n" - + "{[Time].[Time].[1997].[Q1].[2], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Walrus]}\n" - + "{[Time].[Time].[1997].[Q1].[3], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Good]}\n" - + "{[Time].[Time].[1997].[Q1].[3], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Pearl]}\n" - + "{[Time].[Time].[1997].[Q1].[3], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Portsmouth]}\n" - + "{[Time].[Time].[1997].[Q1].[3], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Top Measure]}\n" - + "{[Time].[Time].[1997].[Q1].[3], [Product].[Product].[Drink].[Alcoholic Beverages].[Beer and Wine].[Beer].[Walrus]}\n" - + "Row #0: 2\n" - + "Row #1: 7\n" - + "Row #2: 6\n" - + "Row #3: 7\n" - + "Row #4: 4\n" - + "Row #5: 3\n" - + "Row #6: 4\n" - + "Row #7: 3\n" - + "Row #8: 4\n" - + "Row #9: 2\n" - + "Row #10: \n" - + "Row #11: 4\n" - + "Row #12: \n" - + "Row #13: 2\n" - + "Row #14: \n" - + "Row #15: \n" - + "Row #16: 2\n" - + "Row #17: \n" - + "Row #18: 4\n" - + "Row #19: \n" - + "Row #20: 3\n" - + "Row #21: 4\n" - + "Row #22: 3\n" - + "Row #23: 4\n" - + "Row #24: 2\n"); - } - - /** - * Tests a performance issue with LastNonEmpty (bug 1533677). The naive - * implementation of LastNonEmpty crawls backward one period at a time, - * generates a cache miss, and the next iteration reads precisely one cell. - * So the query soon exceeds the MaxEvalDepth - * property. - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testLastNonEmptyBig(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "with\n" - + " member\n" - + " [Measures].[Last Sale] as ([Measures].[Unit Sales],\n" - + " LastNonEmpty(Descendants([Time].[Time].CurrentMember, [Time].[Month]),\n" - + " [Measures].[Unit Sales]))\n" - + "select\n" - + " NON EMPTY {[Measures].[Last Sale]} ON columns,\n" - + " NON EMPTY Order([Store].[All Stores].Children,\n" - + " [Measures].[Last Sale], DESC) ON rows\n" - + "from [Sales]\n" - + "where [Time].[Time].LastSibling", - "Axis #0:\n" - + "{[Time].[Time].[1998]}\n" - + "Axis #1:\n" - + "Axis #2:\n"); - } - - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testBadFun(Context context) { - updateTestContext(context, SchemaModifiers.UdfTestModifier12::new); - /* - udfTestContext(context, - "\n"); - */ - try { - executeQuery(context.getConnectionWithDefaultRole(), "SELECT {} ON COLUMNS FROM [Sales]"); - fail("Expected exception"); - } catch (Exception e) { - final String s = e.getMessage(); - assertEquals( - "Mondrian Error:Internal error: Invalid " - + "user-defined function 'BadPlusOne': return type is null", s); - } - } - - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testGenericFun(Context context) { - updateTestContext(context, SchemaModifiers.UdfTestModifier14::new); - /* - udfTestContext(context, - "\n" - + "\n"); - */ - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", "GenericPlusOne(3)", "4"); - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", "GenericMinusOne(3)", "2"); - } - - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testComplexFun(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "WITH MEMBER [Measures].[InverseNormal] AS 'InverseNormal([Measures].[Grocery Sqft] / [Measures].[Store Sqft])', FORMAT_STRING = \"0.000\"\n" - + "SELECT {[Measures].[InverseNormal]} ON COLUMNS, \n" - + " {[Store Type].children} ON ROWS \n" - + "FROM [Store]", - - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[InverseNormal]}\n" - + "Axis #2:\n" - + "{[Store Type].[Deluxe Supermarket]}\n" - + "{[Store Type].[Gourmet Supermarket]}\n" - + "{[Store Type].[HeadQuarters]}\n" - + "{[Store Type].[Mid-Size Grocery]}\n" - + "{[Store Type].[Small Grocery]}\n" - + "{[Store Type].[Supermarket]}\n" - + "Row #0: 0.467\n" - + "Row #1: 0.463\n" - + "Row #2: \n" - + "Row #3: 0.625\n" - + "Row #4: 0.521\n" - + "Row #5: 0.504\n"); - } - - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testException(Context context) { - prepareContext(context); - Result result = executeQuery(context.getConnectionWithDefaultRole(), - "WITH MEMBER [Measures].[InverseNormal] " - + " AS 'InverseNormal([Measures].[Store Sqft] / [Measures].[Grocery Sqft])'," - + " FORMAT_STRING = \"0.000000\"\n" - + "SELECT {[Measures].[InverseNormal]} ON COLUMNS, \n" - + " {[Store Type].children} ON ROWS \n" - + "FROM [Store]"); - Axis rowAxis = result.getAxes()[0]; - assertTrue(rowAxis.getPositions().size() == 1); - Axis colAxis = result.getAxes()[1]; - assertTrue(colAxis.getPositions().size() == 6); - Cell cell = result.getCell(new int[]{0, 0}); - assertTrue(cell.isError()); - assertMatchesVerbose( - Pattern.compile( - "(?s).*Invalid value for inverse normal distribution: 1.4708.*"), - cell.getValue().toString()); - cell = result.getCell(new int[]{0, 5}); - assertTrue(cell.isError()); - assertMatchesVerbose( - Pattern.compile( - "(?s).*Invalid value for inverse normal distribution: 1.4435.*"), - cell.getValue().toString()); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateString(Context context) - { - prepareContext(context); - String actual = executeExpr(context.getConnectionWithDefaultRole(), "Sales", "CurrentDateString(\"Ddd mmm dd yyyy\")"); - Date currDate = new Date(); - String dateString = currDate.toString(); - String expected = - dateString.substring(0, 11) - + dateString.substring(dateString.length() - 4); - assertEquals(expected, actual); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberBefore(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[yyyy]\\.[Qq]\\.[m]\", BEFORE)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Time].[Time].[1998].[Q4].[12]}\n" - + "Row #0: \n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberBeforeUsingQuotes(Context context) - { - prepareContext(context); - assertAxisReturns(context.getConnectionWithDefaultRole(), "Sales", - - "CurrentDateMember([Time].[Time], " - + "'\"[Time].[Time].[\"yyyy\"].[Q\"q\"].[\"m\"]\"', BEFORE)", - "[Time].[Time].[1998].[Q4].[12]"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberAfter(Context context) - { - prepareContext(context); - // CurrentDateMember will return null member since the latest date in - // FoodMart is from '98 - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[yyyy]\\.[Qq]\\.[m]\", AFTER)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberExact(Context context) - { - prepareContext(context); - // CurrentDateMember will return null member since the latest date in - // FoodMart is from '98; apply a function on the return value to - // ensure null member instead of null is returned - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[yyyy]\\.[Qq]\\.[m]\", EXACT).lag(1)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberNoFindArg(Context context) - { - prepareContext(context); - // CurrentDateMember will return null member since the latest date in - // FoodMart is from '98 - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[yyyy]\\.[Qq]\\.[m]\", EXACT)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberHierarchy(Context context) { - prepareContext(context); - final String query = "SELECT { CurrentDateMember([Time].[Weekly], " - + "\"[Ti\\me]\\.[Weekl\\y]\\.[All Weekl\\y\\s]\\.[yyyy]\\.[ww]\", BEFORE)} " - + "ON COLUMNS FROM [Sales]"; - assertQueryReturns(context.getConnectionWithDefaultRole(), - query, - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Time].[Weekly].[1998].[52]}\n" - + "Row #0: \n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberHierarchyNullReturn(Context context) { - prepareContext(context); - // CurrentDateMember will return null member since the latest date in - // FoodMart is from '98; note that first arg is a hierarchy rather - // than a dimension - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Weekly], " - + "\"[Ti\\me]\\.[yyyy]\\.[Qq]\\.[m]\", EXACT)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberRealAfter(Context context) { - prepareContext(context); - // omit formatting characters from the format so the current date - // is hard-coded to actual value in the database so we can test the - // after logic - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[1996]\\.[Q4]\", after)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Time].[Time].[1997].[Q1]}\n" - + "Row #0: 66,291\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberRealExact1(Context context) { - prepareContext(context); - // omit formatting characters from the format so the current date - // is hard-coded to actual value in the database so we can test the - // exact logic - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[1997]\", EXACT)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Time].[Time].[1997]}\n" - + "Row #0: 266,773\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberRealExact2(Context context) { - prepareContext(context); - // omit formatting characters from the format so the current date - // is hard-coded to actual value in the database so we can test the - // exact logic - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[1997]\\.[Q2]\\.[5]\", EXACT)} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Time].[Time].[1997].[Q2].[5]}\n" - + "Row #0: 21,081\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateMemberPrev(Context context) { - prepareContext(context); - // apply a function on the result of the UDF - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT { CurrentDateMember([Time].[Time], " - + "\"[Ti\\me]\\.[yyyy]\\.[Qq]\\.[m]\", BEFORE).PrevMember} " - + "ON COLUMNS FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Time].[Time].[1998].[Q4].[11]}\n" - + "Row #0: \n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCurrentDateLag(Context context) { - prepareContext(context); - // Also, try a different style of quoting, because single quote followed - // by double quote (used in other examples) is difficult to read. - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT\n" - + " { [Measures].[Unit Sales] } ON COLUMNS,\n" - + " { CurrentDateMember([Time].[Time], '[\"Time\"]\\.[yyyy]\\.[\"Q\"q]\\.[m]', BEFORE).Lag(3) : " - + " CurrentDateMember([Time].[Time], '[\"Time\"]\\.[yyyy]\\.[\"Q\"q]\\.[m]', BEFORE) } ON ROWS\n" - + "FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "Axis #2:\n" - + "{[Time].[Time].[1998].[Q3].[9]}\n" - + "{[Time].[Time].[1998].[Q4].[10]}\n" - + "{[Time].[Time].[1998].[Q4].[11]}\n" - + "{[Time].[Time].[1998].[Q4].[12]}\n" - + "Row #0: \n" - + "Row #1: \n" - + "Row #2: \n" - + "Row #3: \n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testMatches(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {[Measures].[Org Salary]} ON COLUMNS, " - + "Filter({[Employees].MEMBERS}, " - + "[Employees].CurrentMember.Name MATCHES '(?i)sam.*') ON ROWS " - + "FROM [HR]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Org Salary]}\n" - + "Axis #2:\n" - + "{[Employees].[Employees].[Sheri Nowmer].[Derrick Whelply].[Beverly Baker].[Jacqueline Wyllie].[Ralph Mccoy].[Anne Tuck].[Samuel Johnson]}\n" - + "{[Employees].[Employees].[Sheri Nowmer].[Derrick Whelply].[Pedro Castillo].[Jose Bernard].[Mary Hunt].[Bonnie Bruno].[Sam Warren]}\n" - + "{[Employees].[Employees].[Sheri Nowmer].[Derrick Whelply].[Pedro Castillo].[Charles Macaluso].[Barbara Wallin].[Michael Suggs].[Sam Adair]}\n" - + "{[Employees].[Employees].[Sheri Nowmer].[Derrick Whelply].[Pedro Castillo].[Lois Wood].[Dell Gras].[Kristine Aldred].[Sam Zeller]}\n" - + "{[Employees].[Employees].[Sheri Nowmer].[Derrick Whelply].[Laurie Borges].[Cody Goldey].[Shanay Steelman].[Neal Hasty].[Sam Wheeler]}\n" - + "{[Employees].[Employees].[Sheri Nowmer].[Maya Gutierrez].[Brenda Blumberg].[Wayne Banack].[Samuel Agcaoili]}\n" - + "{[Employees].[Employees].[Sheri Nowmer].[Maya Gutierrez].[Jonathan Murraiin].[James Thompson].[Samantha Weller]}\n" - + "Row #0: $40.62\n" - + "Row #1: $40.31\n" - + "Row #2: $75.60\n" - + "Row #3: $40.35\n" - + "Row #4: $47.52\n" - + "Row #5: \n" - + "Row #6: \n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testNotMatches(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {[Measures].[Store Sales]} ON COLUMNS, " - + "Filter({[Store Type].MEMBERS}, " - + "[Store Type].CurrentMember.Name NOT MATCHES " - + "'.*Grocery.*') ON ROWS " - + "FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Store Sales]}\n" - + "Axis #2:\n" - + "{[Store Type].[Store Type].[All Store Types]}\n" - + "{[Store Type].[Store Type].[Deluxe Supermarket]}\n" - + "{[Store Type].[Store Type].[Gourmet Supermarket]}\n" - + "{[Store Type].[Store Type].[HeadQuarters]}\n" - + "{[Store Type].[Store Type].[Supermarket]}\n" - + "Row #0: 565,238.13\n" - + "Row #1: 162,062.24\n" - + "Row #2: 45,750.24\n" - + "Row #3: \n" - + "Row #4: 319,210.04\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testIn(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {[Measures].[Unit Sales]} ON COLUMNS, " - + "FILTER([Product].[Product Family].MEMBERS, " - + "[Product].[Product Family].CurrentMember IN " - + "{[Product].[All Products].firstChild, " - + "[Product].[All Products].lastChild}) ON ROWS " - + "FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "Axis #2:\n" - + "{[Product].[Product].[Drink]}\n" - + "{[Product].[Product].[Non-Consumable]}\n" - + "Row #0: 24,597\n" - + "Row #1: 50,236\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testNotIn(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {[Measures].[Unit Sales]} ON COLUMNS, " - + "FILTER([Product].[Product Family].MEMBERS, " - + "[Product].[Product Family].CurrentMember NOT IN " - + "{[Product].[All Products].firstChild, " - + "[Product].[All Products].lastChild}) ON ROWS " - + "FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "Axis #2:\n" - + "{[Product].[Product].[Food]}\n" - + "Row #0: 191,940\n"); - } - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testChildMemberIn(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {[Measures].[Store Sales]} ON COLUMNS, " - + "{[Store].[Store Name].MEMBERS} ON ROWS " - + "FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Store Sales]}\n" - + "Axis #2:\n" - + "{[Store].[Store].[Canada].[BC].[Vancouver].[Store 19]}\n" - + "{[Store].[Store].[Canada].[BC].[Victoria].[Store 20]}\n" - + "{[Store].[Store].[Mexico].[DF].[Mexico City].[Store 9]}\n" - + "{[Store].[Store].[Mexico].[DF].[San Andres].[Store 21]}\n" - + "{[Store].[Store].[Mexico].[Guerrero].[Acapulco].[Store 1]}\n" - + "{[Store].[Store].[Mexico].[Jalisco].[Guadalajara].[Store 5]}\n" - + "{[Store].[Store].[Mexico].[Veracruz].[Orizaba].[Store 10]}\n" - + "{[Store].[Store].[Mexico].[Yucatan].[Merida].[Store 8]}\n" - + "{[Store].[Store].[Mexico].[Zacatecas].[Camacho].[Store 4]}\n" - + "{[Store].[Store].[Mexico].[Zacatecas].[Hidalgo].[Store 12]}\n" - + "{[Store].[Store].[Mexico].[Zacatecas].[Hidalgo].[Store 18]}\n" - + "{[Store].[Store].[USA].[CA].[Alameda].[HQ]}\n" - + "{[Store].[Store].[USA].[CA].[Beverly Hills].[Store 6]}\n" - + "{[Store].[Store].[USA].[CA].[Los Angeles].[Store 7]}\n" - + "{[Store].[Store].[USA].[CA].[San Diego].[Store 24]}\n" - + "{[Store].[Store].[USA].[CA].[San Francisco].[Store 14]}\n" - + "{[Store].[Store].[USA].[OR].[Portland].[Store 11]}\n" - + "{[Store].[Store].[USA].[OR].[Salem].[Store 13]}\n" - + "{[Store].[Store].[USA].[WA].[Bellingham].[Store 2]}\n" - + "{[Store].[Store].[USA].[WA].[Bremerton].[Store 3]}\n" - + "{[Store].[Store].[USA].[WA].[Seattle].[Store 15]}\n" - + "{[Store].[Store].[USA].[WA].[Spokane].[Store 16]}\n" - + "{[Store].[Store].[USA].[WA].[Tacoma].[Store 17]}\n" - + "{[Store].[Store].[USA].[WA].[Walla Walla].[Store 22]}\n" - + "{[Store].[Store].[USA].[WA].[Yakima].[Store 23]}\n" - + "Row #0: \n" - + "Row #1: \n" - + "Row #2: \n" - + "Row #3: \n" - + "Row #4: \n" - + "Row #5: \n" - + "Row #6: \n" - + "Row #7: \n" - + "Row #8: \n" - + "Row #9: \n" - + "Row #10: \n" - + "Row #11: \n" - + "Row #12: 45,750.24\n" - + "Row #13: 54,545.28\n" - + "Row #14: 54,431.14\n" - + "Row #15: 4,441.18\n" - + "Row #16: 55,058.79\n" - + "Row #17: 87,218.28\n" - + "Row #18: 4,739.23\n" - + "Row #19: 52,896.30\n" - + "Row #20: 52,644.07\n" - + "Row #21: 49,634.46\n" - + "Row #22: 74,843.96\n" - + "Row #23: 4,705.97\n" - + "Row #24: 24,329.23\n"); - - // test when the member arg is at a different level - // from the set argument - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {[Measures].[Store Sales]} ON COLUMNS, " - + "Filter({[Store].[Store Name].MEMBERS}, " - + "[Store].[Store Name].CurrentMember IN " - + "{[Store].[All Stores].[Mexico], " - + "[Store].[All Stores].[USA]}) ON ROWS " - + "FROM [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Store Sales]}\n" - + "Axis #2:\n"); - } - - /** - * Tests that the inferred return type is correct for a UDF whose return - * type is not the same as would be guessed by the default implementation - * of {@link org.eclipse.daanse.olap.function.def.AbstractFunctionDefinition#getResultType}, which simply - * guesses based on the type of the first argument. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testNonGuessableReturnType(Context context) { - /* - udfTestContext(context, - "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier15::new); - // The default implementation of getResultType would assume that - // StringMult(int, string) returns an int, whereas it returns a string. - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", - "StringMult(5, 'foo') || 'bar'", "foofoofoofoofoobar"); - } - - /** - * Test case for the problem where a string expression gave a - * ClassCastException because it was evaluating to a member, whereas the - * member should have been evaluated to a scalar. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testUdfToString(Context context) { - /* - udfTestContext(context, - "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier15::new); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "with member [Measures].[ABC] as StringMult(1, 'A')\n" - + "member [Measures].[Unit Sales Formatted] as\n" - + " [Measures].[Unit Sales],\n" - + " FORMAT_STRING = '#,###|color=' ||\n" - + " Iif([Measures].[ABC] = 'A', 'red', 'green')\n" - + "select [Measures].[Unit Sales Formatted] on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales Formatted]}\n" - + "Row #0: 266,773|color=red\n"); - } - - /** - * Tests a UDF whose return type is not the same as its first - * parameter. The return type needs to have full dimensional information; - * in this case, HierarchyType(dimension=Time, hierarchy=unknown). - * - *

Also tests applying a UDF to arguments of coercible type. In this - * case, applies f(member,dimension) to args(member,hierarchy). - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testAnotherMemberFun(Context context) { - /* - udfTestContext(context, - "\n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier16::new); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "WITH MEMBER [Measures].[Test] AS " - + "'([Measures].[Store Sales],[Product].[Food],AnotherMemberError([Product].[Drink],[Time].[Time]))'\n" - + "SELECT {[Measures].[Test]} ON COLUMNS, \n" - + " {[Customers].DefaultMember} ON ROWS \n" - + "FROM [Sales]", - - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Test]}\n" - + "Axis #2:\n" - + "{[Customers].[All Customers]}\n" - + "Row #0: 409,035.59\n"); - } - - - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCachingCurrentDate(Context context) { - prepareContext(context); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "SELECT {filter([Time].[Month].Members, " - + "[Time].[Time].CurrentMember in {CurrentDateMember([Time]" - + ".[Time], '[\"Time\"]\\.[yyyy]\\.[\"Q\"q]\\.[m]', " - + "BEFORE)})} ON COLUMNS " - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Time].[Time].[1998].[Q4].[12]}\n" - + "Row #0: \n"); - } - - /** - * Test case for a UDF that returns a list. - * - *

Test case for bug - * MONDRIAN-588, - * "UDF returning List works under 2.4, fails under 3.1.1". - * - *

Also test case for bug - * MONDRIAN-589, - * "UDF expecting List gets anonymous - * mondrian.rolap.RolapNamedSetEvaluator$1 instead". - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testListUdf(Context context) { - prepareContext(context); - checkListUdf(context, ReverseFunction.class); - checkListUdf(context, ReverseIterableFunction.class); - } - - /** - * Helper for {@link #testListUdf()}. - * - * @param functionClass Class that implements the "Reverse" function. - */ - private void checkListUdf(Context context, - final Class functionClass) - { - /* - udfTestContext(context, - "\n"); - */ - CatalogMapping catalogMapping = ((RolapContext) context).getCatalogMapping(); - PojoMappingModifier modifier = new SchemaModifiers.UdfTestModifier17(catalogMapping, functionClass); - updateTestContext(context, modifier); - final String expectedResult = - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Gender].[M]}\n" - + "{[Gender].[F]}\n" - + "{[Gender].[All Gender]}\n" - + "Row #0: 135,215\n" - + "Row #0: 131,558\n" - + "Row #0: 266,773\n"; - // UDF called directly in axis expression. - Connection connection = context.getConnectionWithDefaultRole(); - assertQueryReturns(connection, - "select Reverse([Gender].Members) on 0\n" - + "from [Sales]", - expectedResult); - // UDF as calc set definition - assertQueryReturns(connection, - "with set [Foo] as Reverse([Gender].Members)\n" - + "select [Foo] on 0\n" - + "from [Sales]", - expectedResult); - // UDF applied to calc set -- exhibited MONDRIAN-589 - assertQueryReturns(connection, - "with set [Foo] as [Gender].Members\n" - + "select Reverse([Foo]) on 0\n" - + "from [Sales]", expectedResult); - } - - /** - * Tests that a non-static function gives an error. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testNonStaticUdfFails(Context context) { - /* - udfTestContext(context, - "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier18::new); - assertQueryThrows(context, - "select Reverse2([Gender].Members) on 0\n" + "from [Sales]", - "No function matches signature 'Reverse2()'"); - } - - /** - * Tests a function that takes a member as argument. Want to make sure that - * Mondrian leaves it as a member, does not try to evaluate it to a scalar - * value. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testMemberUdfDoesNotEvaluateToScalar(Context context) { - /* - udfTestContext(context, - "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier19::new); - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", - "MemberName([Gender].[F])", "F"); - } - - /** - * Unit test that ensures that a UDF has either a script or a className. - */ - @Disabled //disabled for CI build TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testUdfNeitherScriptNorClassname(Context context) { - /* - udfTestContext(context, - "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier20::new); - assertQueryThrows(context.getConnectionWithDefaultRole(), - "select from [Sales]", - "Must specify either className attribute or Script element"); - } - - /** - * Unit test that ensures that a UDF does not have both a script - * and a className. - */ - @Disabled //disabled for CI build TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testUdfBothScriptAndClassname(Context context) { - /* - udfTestContext(context, - "\n" - + " \n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier21::new); - assertQueryThrows(context.getConnectionWithDefaultRole(), - "select from [Sales]", - "Must not specify both className attribute and Script element"); - } - - /** - * Unit test that ensures that a UDF has either a script or a className. - */ - @Disabled //disabled for CI build TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testUdfScriptBadLanguage(Context context) { - /* - udfTestContext(context, - "\n" - + " \n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier22::new); - assertQueryThrows(context.getConnectionWithDefaultRole(), - "select from [Sales]", - "Invalid script language 'bad'"); - } - - /** - * Unit test for a UDF defined in JavaScript. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @DisabledIfSystemProperty(named = "tempIgnoreStrageTests",matches = "true") - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testScriptUdf(Context context) { - /* - udfTestContext(context, - "\n" - + " \n" - + "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier23::new); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "with member [Measures].[ABC] as StringMult(1, 'A')\n" - + "member [Measures].[Unit Sales Formatted] as\n" - + " [Measures].[Unit Sales],\n" - + " FORMAT_STRING = '#,###|color=' ||\n" - + " Iif([Measures].[ABC] = 'A', 'red', 'green')\n" - + "select [Measures].[Unit Sales Formatted] on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales Formatted]}\n" - + "Row #0: 266,773|color=red\n"); - } - - /** - * Unit test for a UDF defined in JavaScript, this time the factorial - * function. We also use 'CDATA' section to mask the '<' symbol. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @DisabledIfSystemProperty(named = "tempIgnoreStrageTests",matches = "true") - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testScriptUdfFactorial(Context context) { - //prepareContext(context); - /* - udfTestContext(context, - "\n" - + " \n" - + "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier24::new); - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", - "Factorial(4 + 2)", - "720"); - } - - /** - * Unit test that we get a nice error if a script UDF contains an error. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @DisabledIfSystemProperty(named = "tempIgnoreStrageTests",matches = "true") - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testScriptUdfInvalid(Context context) { - /* - udfTestContext(context, - "\n" - + " \n" - + "\n"); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier25::new); - final Cell cell = executeExprRaw(context.getConnectionWithDefaultRole(), "Sales", "Factorial(4 + 2)"); - assertMatchesVerbose( - Pattern.compile( - "(?s).*ReferenceError: \"factorial_xx\" is not defined..*"), - cell.getValue().toString()); - } - - /** - * Unit test for a cell formatter defined in the old way -- a 'formatter' - * attribute of a Measure element. - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCellFormatter(Context context) { - prepareContext(context); - // Note that - // formatString="Standard" - // is ignored. - /* - measureTestContext(context, - ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier1::new); - assertQueryReturns(context.getConnectionWithDefaultRole(), - "select {[Measures].[Unit Sales],\n" - + " [Measures].[Unit Sales Foo Bar]} on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "{[Measures].[Unit Sales Foo Bar]}\n" - + "Row #0: 266,773\n" - + "Row #0: foo266773.0bar\n"); - } - - /** - * As {@link #testCellFormatter()}, but using new-style nested - * CellFormatter element. - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCellFormatterNested(Context context) { - prepareContext(context); - // Note that - // formatString="Standard" - // is ignored. - /* - measureTestContext(context, - "\n" - + " \n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier1::new); - - assertQueryReturns(context.getConnectionWithDefaultRole(), - "select {[Measures].[Unit Sales],\n" - + " [Measures].[Unit Sales Foo Bar]} on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "{[Measures].[Unit Sales Foo Bar]}\n" - + "Row #0: 266,773\n" - + "Row #0: foo266773.0bar\n"); - } - - /** - * As {@link #testCellFormatterNested()}, but using a script. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCellFormatterScript(Context context) { - /* - measureTestContext(context, - "\n" - + " \n" - + " \n" - + " \n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier2::new); - - // Note that the result is slightly different to above (a missing ".0"). - // Not a great concern -- in fact it proves that the scripted UDF is - // being used. - assertQueryReturns(context.getConnectionWithDefaultRole(), - "select {[Measures].[Unit Sales],\n" - + " [Measures].[Unit Sales Foo Bar]} on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "{[Measures].[Unit Sales Foo Bar]}\n" - + "Row #0: 266,773\n" - + "Row #0: foo266773bar\n"); - } - - /** - * Unit test for a cell formatter defined against a calculated member, - * using the old syntax (a member property called "CELL_FORMATTER"). - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCellFormatterOnCalcMember(Context context) { - /* - calcMemberTestContext(context, - "\n" - + " [Measures].[Unit Sales]\n" - + " \n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier3::new); - - assertQueryReturns(context.getConnectionWithDefaultRole(), - "select {[Measures].[Unit Sales],\n" - + " [Measures].[Unit Sales Foo Bar]} on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "{[Measures].[Unit Sales Foo Bar]}\n" - + "Row #0: 266,773\n" - + "Row #0: foo266773.0bar\n"); - } - - /** - * Unit test for a cell formatter defined against a calculated member, - * using the new syntax (a nested CellFormatter element). - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCellFormatterOnCalcMemberNested(Context context) { - /* - calcMemberTestContext(context, - "\n" - + " [Measures].[Unit Sales]\n" - + " \n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier4::new); - - assertQueryReturns(context.getConnectionWithDefaultRole(), - "select {[Measures].[Unit Sales],\n" - + " [Measures].[Unit Sales Foo Bar]} on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "{[Measures].[Unit Sales Foo Bar]}\n" - + "Row #0: 266,773\n" - + "Row #0: foo266773.0bar\n"); - } - - /** - * Unit test for a cell formatter defined against a calculated member, - * using a script. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testCellFormatterOnCalcMemberScript(Context context) { - prepareContext(context); - /* - calcMemberTestContext(context, - "\n" - + " [Measures].[Unit Sales]\n" - + " \n" - + " \n" - + " \n" - + ""); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier5::new); - - assertQueryReturns(context.getConnectionWithDefaultRole(), - "select {[Measures].[Unit Sales],\n" - + " [Measures].[Unit Sales Foo Bar]} on 0\n" - + "from [Sales]", - "Axis #0:\n" - + "{}\n" - + "Axis #1:\n" - + "{[Measures].[Unit Sales]}\n" - + "{[Measures].[Unit Sales Foo Bar]}\n" - + "Row #0: 266,773\n" - + "Row #0: foo266773bar\n"); - } - - /** - * Unit test for a member formatter defined in the old way -- a 'formatter' - * attribute of a Measure element. - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testMemberFormatter(Context context) { - prepareContext(context); - /* - ((BaseTestContext)context).update(SchemaUpdater.createSubstitutingCube( - "Sales", - " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " ")); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier6::new); - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", - "[Promotion Media2].FirstChild.Caption", - "fooBulk Mailbar"); - } - - /** - * As {@link #testMemberFormatter()}, but using new-style nested - * memberFormatter element. - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testMemberFormatterNested(Context context) { - prepareContext(context); - /* - ((BaseTestContext)context).update(SchemaUpdater.createSubstitutingCube( - "Sales", - " \n" - + " \n" - + "
\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " ")); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier6::new); - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", - "[Promotion Media2].FirstChild.Caption", - "fooBulk Mailbar"); - } - - /** - * As {@link #testMemberFormatterNested()}, but using a script. - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testMemberFormatterScript(Context context) { - prepareContext(context); - /* - ((BaseTestContext)context).update(SchemaUpdater.createSubstitutingCube( - "Sales", - " \n" - + " \n" - + "
\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " ")); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier7::new); - assertExprReturns(context.getConnectionWithDefaultRole(), "Sales", - "[Promotion Media2].FirstChild.Caption", - "fooBulk Mailbar"); - } - - /** - * Unit test for a property formatter defined in the old way -- a - * 'formatter' attribute of a Property element. - * - * @throws SQLException on error - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testPropertyFormatter(Context context) throws SQLException { - prepareContext(context); - /* - ((BaseTestContext)context).update(SchemaUpdater.createSubstitutingCube( - "Sales", - "\n" - + " \n" - + "
\n" - + " \n" - + " \n" - + " \n" - + " \n" - + "")); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier8::new); - final CellSet result = - executeOlap4jQuery(context.getConnectionWithDefaultRole(), - "select [Promotions2].Children on 0\n" - + "from [Sales]"); - final Member member = - result.getAxes().get(0).getPositions().get(0).getMembers().get(0); - final org.eclipse.daanse.olap.api.element.Property property = Arrays.stream(member.getProperties()).filter(p -> "Medium".equals(p.getName())).findFirst() - .orElseThrow(() -> new RuntimeException("property with name \"Medium\" is absent")); - assertEquals( - "foo0/Medium/No Mediabar", - member.getPropertyFormattedValue(property.getName())); - } - - /** - * As {@link #testPropertyFormatter()}, but using new-style nested - * PropertyFormatter element. - * - * @throws SQLException on error - */ - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testPropertyFormatterNested(Context context) throws SQLException { - prepareContext(context); - /* - ((BaseTestContext)context).update(SchemaUpdater.createSubstitutingCube( - "Sales", - "\n" - + " \n" - + "
\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "")); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier9::new); - - final CellSet result = - executeOlap4jQuery(context.getConnectionWithDefaultRole(), - "select [Promotions2].Children on 0\n" - + "from [Sales]"); - final Member member = - result.getAxes().get(0).getPositions().get(0).getMembers().get(0); - final Property property = Arrays.stream(member.getProperties()) - .filter(p -> "Medium".equals(p.getName())).findFirst() - .orElseThrow(() -> new RuntimeException("Property with name \"Medium\" is absent")); - assertEquals( - "foo0/Medium/No Mediabar", - member.getPropertyFormattedValue(property.getName())); - } - - /** - * As {@link #testPropertyFormatterNested()}, but using a script. - * - * @throws SQLException on error - */ - @Disabled //TODO: UserDefinedFunction - @ParameterizedTest - @ContextSource(propertyUpdater = AppandFoodMartCatalog.class, dataloader = FastFoodmardDataLoader.class) - void testPropertyFormatterScript(Context context) throws SQLException { - prepareContext(context); - /* - ((BaseTestContext)context).update(SchemaUpdater.createSubstitutingCube( - "Sales", - "\n" - + " \n" - + "
\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "")); - */ - updateTestContext(context, SchemaModifiers.UdfTestModifier10::new); - - final CellSet result = - executeOlap4jQuery(context.getConnectionWithDefaultRole(), - "select [Promotions2].Children on 0\n" - + "from [Sales]"); - final Member member = - result.getAxes().get(0).getPositions().get(0).getMembers().get(0); - final - org.eclipse.daanse.olap.api.element.Property property = Arrays.stream(member.getProperties()).filter(p -> "Medium".equals(p.getName())) - .findFirst().orElseThrow(() -> new RuntimeException("Property with name \"Medium\" is absent")); - assertEquals( - "foo0/Medium/No Mediabar", - member.getPropertyFormattedValue(property.getName())); - } - - - /** - * A simple user-defined function which adds one to its argument. - */ - public static class PlusOneUdf implements UserDefinedFunction { - @Override - public String getName() { - return "PlusOne"; - } - - @Override - public String getDescription() { - return "Returns its argument plus one"; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - - @Override - public Type getReturnType(Type[] parameterTypes) { - return NumericType.INSTANCE; - } - - @Override - public Type[] getParameterTypes() { - return new Type[] {NumericType.INSTANCE}; - } - - @Override - public Object execute(Evaluator evaluator, Argument[] arguments) { - final Object argValue = arguments[0].evaluateScalar(evaluator); - if (argValue instanceof Number) { - return ((Number) argValue).doubleValue() + 1.0; - } else { - // Argument might be a RuntimeException indicating that - // the cache does not yet have the required cell value. The - // function will be called again when the cache is loaded. - return null; - } - } - - - } - - /** - * A simple user-defined function which adds one to its argument. - */ - public static class BadPlusOneUdf extends PlusOneUdf { - private final String name; - - public BadPlusOneUdf(String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public Type getReturnType(Type[] parameterTypes) { - // Will cause error. - return null; - } - } - - /** - * A user-defined function which, depending on its given name, either adds - * one to, or subtracts one from, its argument. - */ - public static class PlusOrMinusOneUdf implements UserDefinedFunction { - private final String name; - - public PlusOrMinusOneUdf(String name) { - if (!(name.equals("GenericPlusOne") - || name.equals("GenericMinusOne"))) - { - throw new IllegalArgumentException(); - } - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getDescription() { - return - "A user-defined function which, depending on its given name, " - + "either addsone to, or subtracts one from, its argument"; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - - @Override - public Type getReturnType(Type[] parameterTypes) { - return NumericType.INSTANCE; - } - - - - @Override - public Type[] getParameterTypes() { - return new Type[] {NumericType.INSTANCE}; - } - - @Override - public Object execute(Evaluator evaluator, Argument[] arguments) { - final Object argValue = arguments[0].evaluateScalar(evaluator); - if (argValue instanceof Number) { - return ((Number) argValue).doubleValue() - + (name.equals("GenericPlusOne") ? 1.0 : -1.0); - } else { - // Argument might be a RuntimeException indicating that - // the cache does not yet have the required cell value. The - // function will be called again when the cache is loaded. - return null; - } - } - } - - /** - * The "TimesString" user-defined function. We wanted a function whose - * actual return type (string) is not the same as the guessed return type - * (integer). - */ - public static class StringMultUdf implements UserDefinedFunction { - @Override - public String getName() { - return "StringMult"; - } - - @Override - public String getDescription() { - return "Returns N copies of its string argument"; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - - @Override - public Type getReturnType(Type[] parameterTypes) { - return StringType.INSTANCE; - } - - @Override - public Type[] getParameterTypes() { - return new Type[] { - NumericType.INSTANCE, StringType.INSTANCE - }; - } - - @Override - public Object execute(Evaluator evaluator, Argument[] arguments) { - final Object argValue = arguments[0].evaluateScalar(evaluator); - int n; - if (argValue instanceof Number) { - n = ((Number) argValue).intValue(); - } else { - // Argument might be a RuntimeException indicating that - // the cache does not yet have the required cell value. The - // function will be called again when the cache is loaded. - return null; - } - String s; - final Object argValue2 = arguments[1].evaluateScalar(evaluator); - if (argValue2 instanceof String) { - s = (String) argValue2; - } else { - return null; - } - if (n < 0) { - return null; - } - StringBuilder buf = new StringBuilder(s.length() * n); - for (int i = 0; i < n; i++) { - buf.append(s); - } - return buf.toString(); - } - - - } - - /** - * A user-defined function which returns ignores its first parameter (a - * member) and returns the default member from the second parameter (a - * hierarchy). - */ - public static class AnotherMemberErrorUdf implements UserDefinedFunction { - @Override - public String getName() { - return "AnotherMemberError"; - } - - @Override - public String getDescription() { - return "Returns default member from hierarchy, " - + "specified as a second parameter. " - + "First parameter - any member from any hierarchy"; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - - @Override - public Type getReturnType(Type[] parameterTypes) { - HierarchyType hierType = (HierarchyType) parameterTypes[1]; - return MemberType.forType(hierType); - } - - @Override - public Type[] getParameterTypes() { - return new Type[] { - // The first argument must be a member. - MemberType.Unknown, - // The second argument must be a hierarchy. - HierarchyType.Unknown - }; - } - - @Override - public Object execute(Evaluator evaluator, Argument[] arguments) { - // Simply ignore first parameter - Member member = (Member)arguments[0].evaluate(evaluator); -// discard(member); - Hierarchy hierarchy = (Hierarchy)arguments[1].evaluate(evaluator); - return hierarchy.getDefaultMember(); - } - - - } - - /** - * Function that reverses a list of members. - */ - public static class ReverseFunction implements UserDefinedFunction { - @Override - public Object execute(Evaluator eval, Argument[] args) { - // Note: must call Argument.evaluateList. If we call - // Argument.evaluate we may get an Iterable. - List list = args[0].evaluateList(eval); - // We do need to copy before we reverse. The list is not guaranteed - // to be mutable. - list = new ArrayList(list); - Collections.reverse(list); - return list; - } - - @Override - public String getDescription() { - return "Reverses the order of a set"; - } - - @Override - public String getName() { - return "Reverse"; - } - - @Override - public Type[] getParameterTypes() { - return new Type[] {new SetType(MemberType.Unknown)}; - } - - - - @Override - public Type getReturnType(Type[] arg0) { - return arg0[0]; - } -// -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - } - - /** - * Function that is non-static. - */ - public class ReverseFunctionNotStatic extends ReverseFunction { - } - - /** - * Function that takes a set of members as argument, and returns a set of - * members. - */ - public static class ReverseIterableFunction extends ReverseFunction { - @Override - public Object execute(Evaluator eval, Argument[] args) { - // Note: must call Argument.evaluateList. If we call - // Argument.evaluate we may get an Iterable. - Iterable iterable = args[0].evaluateIterable(eval); - List list = new ArrayList<>(); - for (Object o : iterable) { - list.add(o); - } - Collections.reverse(list); - return list; - } - } - - /** - * Function that takes a member and returns a name. - */ - public static class MemberNameFunction implements UserDefinedFunction { - @Override - public Object execute(Evaluator eval, Argument[] args) { - Member member = (Member) args[0].evaluate(eval); - return member.getName(); - } - - @Override - public String getDescription() { - return "Returns the name of a member"; - } - - @Override - public String getName() { - return "MemberName"; - } - - @Override - public Type[] getParameterTypes() { - return new Type[] {MemberType.Unknown}; - } - - @Override - public Type getReturnType(Type[] arg0) { - return StringType.INSTANCE; - } - -// @Override -// public Syntax getSyntax() { -// return Syntax.Function; -// } - } - - /** - * Member formatter for test purposes. Returns name of the member prefixed - * with "foo" and suffixed with "bar". - */ - public static class FooBarMemberFormatter implements MemberFormatter { - @Override - public String format(Member member) { - return "foo" + member.getName() + "bar"; - } - } - - /** - * Cell formatter for test purposes. Returns value of the cell prefixed - * with "foo" and suffixed with "bar". - */ - public static class FooBarCellFormatter implements CellFormatter { - @Override - public String format(Object value) { - return "foo" + value + "bar"; - } - } - - /** - * Property formatter for test purposes. Returns name of the member and - * property, then the value, prefixed with "foo" and suffixed with "bar". - */ - public static class FooBarPropertyFormatter implements MemberPropertyFormatter { - @Override - public String format( - Member member, Property property, Object propertyValue) - { - return "foo" + member.getName() + "/" + property.getName() + "/" - + propertyValue + "bar"; - } - } -}