blob: ac0f2c837541789ac9f1cff129ef9bc476192e65 [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.compile.WindowFunctionNode
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.derby.impl.sql.compile;
import java.util.List;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.compile.CompilerContext;
/**
* Superclass of any window function call.
*/
public abstract class WindowFunctionNode extends UnaryOperatorNode
{
private WindowNode window; // definition or reference
/*
** We wind up pushing all window function calls into a different
** resultColumnList. When we do this (in replaceCallsWithColumnReference),
** we return a column reference and create a new result column. This is
** used to store that result column.
*/
private ResultColumn generatedRC;
private ColumnReference generatedRef;
/**
* @param op operand (null for now)
* @param functionName operator
* @param w window node (definition or reference)
* @param cm context manager
* @throws StandardException
*/
WindowFunctionNode(
ValueNode op, String functionName, WindowNode w, ContextManager cm)
throws StandardException {
super(op, functionName, (String)null, cm);
this.window = w;
}
/**
* ValueNode override.
* @see ValueNode#isConstantExpression
*/
@Override
public boolean isConstantExpression()
{
return false;
}
/**
* ValueNode override.
* @see ValueNode#isConstantExpression
*/
@Override
boolean constantExpression(PredicateList whereClause)
{
// Without this, an ORDER by on ROW_NUMBER could get optimised away
// if there is a restriction, e.g.
//
// SELECT -ABS(i) a, ROW_NUMBER() OVER () c FROM t
// WHERE i > 1 ORDER BY c DESC
return false;
}
/**
* @return window associated with this window function
*/
WindowNode getWindow() {
return window;
}
/**
* Set window associated with this window function call.
* @param wdn window definition
*/
void setWindow(WindowDefinitionNode wdn) {
this.window = wdn;
}
/**
* ValueNode override.
* @see ValueNode#bindExpression
*/
@Override
ValueNode bindExpression(
FromList fromList, SubqueryList subqueryList, List<AggregateNode> aggregates)
throws StandardException
{
if (window instanceof WindowReferenceNode) {
WindowDefinitionNode found =
definedWindow(fromList.getWindows(), window.getName());
if (found != null) {
window = found;
} else {
throw StandardException.
newException(SQLState.LANG_NO_SUCH_WINDOW,
window.getName());
}
}
return this;
}
/**
* @return if name matches a defined window (in windows), return the
* definition of that window, else null.
*/
private WindowDefinitionNode definedWindow(WindowList windows,
String name) {
for (WindowDefinitionNode wdn : windows) {
if (wdn.getName().equals(name)) {
return wdn;
}
}
return null;
}
/**
* QueryTreeNode override.
* @see QueryTreeNode#printSubNodes
*/
@Override
public void printSubNodes(int depth)
{
if (SanityManager.DEBUG)
{
super.printSubNodes(depth);
printLabel(depth, "window: ");
window.treePrint(depth + 1);
}
}
/**
* Replace window function calls in the expression tree with a
* ColumnReference to that window function, append the aggregate to the
* supplied RCL (assumed to be from the child ResultSetNode) and return the
* ColumnReference.
*
* @param rcl The RCL to append to.
* @param tableNumber The tableNumber for the new ColumnReference
*
* @return ValueNode The (potentially) modified tree.
*
* @exception StandardException Thrown on error
*/
ValueNode replaceCallsWithColumnReferences(ResultColumnList rcl,
int tableNumber)
throws StandardException
{
/*
* This call is idempotent. Do the right thing if we have already
* replaced ourselves.
*/
if (generatedRef == null)
{
String generatedColName;
CompilerContext cc = getCompilerContext();
generatedColName ="SQLCol" + cc.getNextColumnNumber();
generatedRC = new ResultColumn(
generatedColName, this, getContextManager());
generatedRC.markGenerated();
// Parse time.
//
generatedRef = new ColumnReference(
generatedRC.getName(), null, getContextManager());
// RESOLVE - unknown nesting level, but not correlated, so nesting
// levels must be 0
generatedRef.setSource(generatedRC);
generatedRef.setNestingLevel(0);
generatedRef.setSourceLevel(0);
if (tableNumber != -1)
{
generatedRef.setTableNumber(tableNumber);
}
rcl.addResultColumn(generatedRC);
// Mark the ColumnReference as being generated to replace a call to
// a window function
generatedRef.markGeneratedToReplaceWindowFunctionCall();
}
else
{
rcl.addResultColumn(generatedRC);
}
return generatedRef;
}
/**
* Get the generated ColumnReference to this window function after the
* parent called replaceCallsWithColumnReferences().
* <p/>
* There are cases where this will not have been done because the tree has
* been re-written to eliminate the window function, e.g. for this query:
* <p/><pre>
* {@code SELECT * FROM t WHERE EXISTS
* (SELECT ROW_NUMBER() OVER () FROM t)}
* </pre><p/>
* in which case the top PRN of the subquery sitting over a
* WindowResultSetNode just contains a RC which is boolean constant {@code
* true}. This means that the replaceCallsWithColumnReferences will not
* have been called for {@code this}, so the returned {@code generatedRef}
* is null.
*
* @return the column reference
*/
ColumnReference getGeneratedRef()
{
return generatedRef;
}
/**
* Get the null result expression column.
*
* @return the value node
*
* @exception StandardException on error
*/
ValueNode getNewNullResultExpression()
throws StandardException
{
//
// Create a result column with the aggregate operand
// it.
//
return getNullNode(getTypeServices());
}
}