KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > compile > OrderByColumn


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.OrderByColumn
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.compile;
23
24 import org.apache.derby.iapi.types.TypeId;
25
26 import org.apache.derby.iapi.error.StandardException;
27 import org.apache.derby.iapi.reference.SQLState;
28
29 import org.apache.derby.iapi.services.sanity.SanityManager;
30
31 import org.apache.derby.iapi.sql.compile.NodeFactory;
32 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
33
34 import org.apache.derby.iapi.util.ReuseFactory;
35
36 /**
37  * An OrderByColumn is a column in the ORDER BY clause. An OrderByColumn
38  * can be ordered ascending or descending.
39  *
40  * We need to make sure that the named columns are
41  * columns in that query, and that positions are within range.
42  *
43  * @author ames
44  */

45 public class OrderByColumn extends OrderedColumn {
46
47     private ResultColumn resultCol;
48     private boolean ascending = true;
49     private ValueNode expression;
50     /**
51      * If this sort key is added to the result column list then it is at result column position
52      * 1 + resultColumnList.size() - resultColumnList.getOrderBySelect() + addedColumnOffset
53      * If the sort key is already in the result column list then addedColumnOffset < 0.
54      */

55     private int addedColumnOffset = -1;
56
57
58     /**
59      * Initializer.
60      *
61      * @param expression Expression of this column
62      */

63     public void init(Object JavaDoc expression)
64     {
65         this.expression = (ValueNode)expression;
66     }
67     
68     /**
69      * Convert this object to a String. See comments in QueryTreeNode.java
70      * for how this should be done for tree printing.
71      *
72      * @return This object as a String
73      */

74     public String JavaDoc toString() {
75         if (SanityManager.DEBUG) {
76             return expression.toString();
77         } else {
78             return "";
79         }
80     }
81
82     /**
83      * Mark the column as descending order
84      */

85     public void setDescending() {
86         ascending = false;
87     }
88
89     /**
90      * Get the column order. Overrides
91      * OrderedColumn.isAscending.
92      *
93      * @return true if ascending, false if descending
94      */

95     public boolean isAscending() {
96         return ascending;
97     }
98
99     /**
100      * Get the underlying ResultColumn.
101      *
102      * @return The underlying ResultColumn.
103      */

104     ResultColumn getResultColumn()
105     {
106         return resultCol;
107     }
108
109     /**
110      * Get the underlying expression, skipping over ResultColumns that
111      * are marked redundant.
112      */

113     ValueNode getNonRedundantExpression()
114     {
115         ResultColumn rc;
116         ValueNode value;
117         ColumnReference colref = null;
118
119         for (rc = resultCol; rc.isRedundant(); rc = colref.getSource())
120         {
121             value = rc.getExpression();
122
123             if (value instanceof ColumnReference)
124             {
125                 colref = (ColumnReference) value;
126             }
127             else
128             {
129                 if (SanityManager.DEBUG)
130                 {
131                     SanityManager.THROWASSERT(
132                         "value should be a ColumnReference, but is a " +
133                         value.getClass().getName());
134                 }
135             }
136         }
137
138         return rc.getExpression();
139     }
140
141     /**
142      * Bind this column.
143      *
144      * @param target The result set being selected from
145      *
146      * @exception StandardException Thrown on error
147      * @exception StandardException Thrown when column not found
148      */

149     public void bindOrderByColumn(ResultSetNode target)
150                 throws StandardException
151     {
152         if(expression instanceof ColumnReference){
153         
154             ColumnReference cr = (ColumnReference) expression;
155             
156             resultCol = resolveColumnReference(target,
157                                cr);
158             
159             columnPosition = resultCol.getColumnPosition();
160
161         }else if(isReferedColByNum(expression)){
162             
163             ResultColumnList targetCols = target.getResultColumns();
164             columnPosition = ((Integer JavaDoc)expression.getConstantValueAsObject()).intValue();
165             resultCol = targetCols.getOrderByColumn(columnPosition);
166             
167             if (resultCol == null) {
168                 throw StandardException.newException(SQLState.LANG_COLUMN_OUT_OF_RANGE,
169                                      String.valueOf(columnPosition));
170             }
171
172         }else{
173             if( SanityManager.DEBUG)
174                 SanityManager.ASSERT( addedColumnOffset >= 0,
175                                       "Order by expression was not pulled into the result column list");
176             resolveAddedColumn(target);
177         }
178
179         // Verify that the column is orderable
180
resultCol.verifyOrderable();
181     }
182
183     private void resolveAddedColumn(ResultSetNode target)
184     {
185         ResultColumnList targetCols = target.getResultColumns();
186         columnPosition = targetCols.size() - targetCols.getOrderBySelect() + addedColumnOffset + 1;
187         resultCol = targetCols.getResultColumn( columnPosition);
188     }
189
190     /**
191      * Pull up this orderby column if it doesn't appear in the resultset
192      *
193      * @param target The result set being selected from
194      *
195      */

196     public void pullUpOrderByColumn(ResultSetNode target)
197                 throws StandardException
198     {
199         ResultColumnList targetCols = target.getResultColumns();
200
201         // If the target is generated for a select node then we must also pull the order by column
202
// into the select list of the subquery.
203
if((target instanceof SelectNode) && ((SelectNode) target).getGeneratedForGroupbyClause())
204         {
205             if( SanityManager.DEBUG)
206                 SanityManager.ASSERT( target.getFromList().size() == 1
207                                       && (target.getFromList().elementAt(0) instanceof FromSubquery)
208                                       && targetCols.size() == 1
209                                       && targetCols.getResultColumn(1) instanceof AllResultColumn,
210                                       "Unexpected structure of selectNode generated for a group by clause");
211
212             ResultSetNode subquery = ((FromSubquery) target.getFromList().elementAt(0)).getSubquery();
213             pullUpOrderByColumn( subquery);
214             if( resultCol == null) // The order by column is referenced by number
215
return;
216
217             // ResultCol is in the subquery's ResultColumnList. We have to transform this OrderByColumn
218
// so that it refers to the column added to the subquery. We assume that the select list
219
// in the top level target is a (generated) AllResultColumn node, so the this order by expression
220
// does not have to be pulled into the the top level ResultColumnList. Just change this
221
// OrderByColumn to be a reference to the added column. We cannot use an integer column
222
// number because the subquery can have a '*' in its select list, causing the column
223
// number to change when the '*' is expanded.
224
resultCol = null;
225             targetCols.copyOrderBySelect( subquery.getResultColumns());
226             return;
227         }
228
229         if(expression instanceof ColumnReference){
230
231             ColumnReference cr = (ColumnReference) expression;
232
233             resultCol = targetCols.getOrderByColumn(cr.getColumnName(),
234                                                     cr.getTableNameNode());
235
236             if(resultCol == null){
237                 resultCol = (ResultColumn) getNodeFactory().getNode(C_NodeTypes.RESULT_COLUMN,
238                                             cr.getColumnName(),
239                                             cr,
240                                             getContextManager());
241                 targetCols.addResultColumn(resultCol);
242                 addedColumnOffset = targetCols.getOrderBySelect();
243                 targetCols.incOrderBySelect();
244             }
245             
246         }else if(!isReferedColByNum(expression)){
247             resultCol = (ResultColumn) getNodeFactory().getNode(C_NodeTypes.RESULT_COLUMN,
248                                         null,
249                                         expression,
250                                         getContextManager());
251             targetCols.addResultColumn(resultCol);
252             addedColumnOffset = targetCols.getOrderBySelect();
253             targetCols.incOrderBySelect();
254         }
255     }
256
257     /**
258      * Order by columns now point to the PRN above the node of interest.
259      * We need them to point to the RCL under that one. This is useful
260      * when combining sorts where we need to reorder the sorting
261      * columns.
262      */

263     void resetToSourceRC()
264     {
265         if (SanityManager.DEBUG)
266         {
267             if (! (resultCol.getExpression() instanceof VirtualColumnNode))
268             {
269                 SanityManager.THROWASSERT(
270                     "resultCol.getExpression() expected to be instanceof VirtualColumnNode " +
271                     ", not " + resultCol.getExpression().getClass().getName());
272             }
273         }
274
275         VirtualColumnNode vcn = (VirtualColumnNode) resultCol.getExpression();
276         resultCol = vcn.getSourceResultColumn();
277     }
278
279     /**
280      * Is this OrderByColumn constant, according to the given predicate list?
281      * A constant column is one where all the column references it uses are
282      * compared equal to constants.
283      */

284     boolean constantColumn(PredicateList whereClause)
285     {
286         ValueNode sourceExpr = resultCol.getExpression();
287
288         return sourceExpr.constantExpression(whereClause);
289     }
290
291     /**
292      * Remap all the column references under this OrderByColumn to their
293      * expressions.
294      *
295      * @exception StandardException Thrown on error
296      */

297     void remapColumnReferencesToExpressions() throws StandardException
298     {
299         resultCol.setExpression(
300             resultCol.getExpression().remapColumnReferencesToExpressions());
301     }
302
303     private static boolean isReferedColByNum(ValueNode expression)
304     throws StandardException{
305         
306         if(!expression.isConstantExpression()){
307             return false;
308         }
309         
310         return expression.getConstantValueAsObject() instanceof Integer JavaDoc;
311     }
312
313     
314     private ResultColumn resolveColumnReference(ResultSetNode target,
315                                ColumnReference cr)
316     throws StandardException{
317         
318         ResultColumn resultCol = null;
319         
320         int sourceTableNumber = -1;
321         
322         //bug 5716 - for db2 compatibility - no qualified names allowed in order by clause when union/union all operator is used
323

324         if (target instanceof SetOperatorNode && cr.getTableName() != null){
325             String JavaDoc fullName = cr.getSQLColumnName();
326             throw StandardException.newException(SQLState.LANG_QUALIFIED_COLUMN_NAME_NOT_ALLOWED, fullName);
327         }
328
329         if(cr.getTableNameNode() != null){
330             TableName tableNameNode = cr.getTableNameNode();
331
332             FromTable fromTable = target.getFromTableByName(tableNameNode.getTableName(),
333                                     (tableNameNode.hasSchema() ?
334                                      tableNameNode.getSchemaName():null),
335                                     true);
336             if(fromTable == null){
337                 fromTable = target.getFromTableByName(tableNameNode.getTableName(),
338                                       (tableNameNode.hasSchema() ?
339                                        tableNameNode.getSchemaName():null),
340                                       false);
341                 if(fromTable == null){
342                     String JavaDoc fullName = cr.getTableNameNode().toString();
343                     throw StandardException.newException(SQLState.LANG_EXPOSED_NAME_NOT_FOUND, fullName);
344                 }
345             }
346
347             /* HACK - if the target is a UnionNode, then we have to
348              * have special code to get the sourceTableNumber. This is
349              * because of the gyrations we go to with building the RCLs
350              * for a UnionNode.
351              */

352             if (target instanceof SetOperatorNode)
353             {
354                 sourceTableNumber = ((FromTable) target).getTableNumber();
355             }
356             else
357             {
358                 sourceTableNumber = fromTable.getTableNumber();
359             }
360             
361         }
362
363         ResultColumnList targetCols = target.getResultColumns();
364
365         resultCol = targetCols.getOrderByColumn(cr.getColumnName(),
366                             cr.getTableNameNode(),
367                             sourceTableNumber);
368         /* Search targetCols before using addedColumnOffset because select list wildcards, '*',
369          * are expanded after pullUpOrderByColumn is called. A simple column reference in the
370          * order by clause may be found in the user specified select list now even though it was
371          * not found when pullUpOrderByColumn was called.
372          */

373         if( resultCol == null && addedColumnOffset >= 0)
374             resolveAddedColumn(target);
375                             
376         if (resultCol == null || resultCol.isNameGenerated()){
377             String JavaDoc errString = cr.columnName;
378             throw StandardException.newException(SQLState.LANG_ORDER_BY_COLUMN_NOT_FOUND, errString);
379         }
380
381         return resultCol;
382
383     }
384
385 }
386
Popular Tags