EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.table]

COVERAGE SUMMARY FOR SOURCE FILE [TableFilter.java]

nameclass, %method, %block, %line, %
TableFilter.java100% (5/5)96%  (65/68)93%  (1504/1618)91%  (368.7/403)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TableFilter100% (1/1)95%  (57/60)93%  (1455/1569)91%  (360.7/395)
removeFilterCondition (): void 0%   (0/1)0%   (0/4)0%   (0/2)
setAlias (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
toString (): String 0%   (0/1)0%   (0/10)0%   (0/1)
setEvaluatable (TableFilter): void 100% (1/1)30%  (10/33)25%  (3/12)
addFilterCondition (Expression, boolean): void 100% (1/1)72%  (26/36)88%  (7/8)
addJoin (TableFilter, boolean, boolean, Expression): void 100% (1/1)77%  (85/111)72%  (21/29)
getRowIdColumn (): Column 100% (1/1)83%  (10/12)67%  (2/3)
mapAndAddFilter (Expression): void 100% (1/1)89%  (31/35)89%  (8/9)
getPlanSQL (boolean): String 100% (1/1)93%  (246/265)94%  (51.7/55)
prepare (): void 100% (1/1)93%  (80/86)90%  (19/21)
getValue (Column): Value 100% (1/1)95%  (41/43)92%  (12/13)
setPlanItem (PlanItem): void 100% (1/1)97%  (29/30)90%  (9/10)
next (): boolean 100% (1/1)98%  (170/173)96%  (45/47)
TableFilter (Session, Table, String, boolean, Select): void 100% (1/1)100% (35/35)100% (11/11)
addIndexCondition (IndexCondition): void 100% (1/1)100% (6/6)100% (2/2)
addNaturalJoinColumn (Column): void 100% (1/1)100% (12/12)100% (4/4)
checkTimeout (): void 100% (1/1)100% (4/4)100% (2/2)
get (): Row 100% (1/1)100% (14/14)100% (3/3)
getBestPlanItem (Session, int): PlanItem 100% (1/1)100% (155/155)100% (29/29)
getColumns (): Column [] 100% (1/1)100% (4/4)100% (1/1)
getFilterCondition (): Expression 100% (1/1)100% (3/3)100% (1/1)
getIndex (): Index 100% (1/1)100% (3/3)100% (1/1)
getIndexConditionsForColumn (Column): ArrayList 100% (1/1)100% (28/28)100% (6/6)
getJoin (): TableFilter 100% (1/1)100% (3/3)100% (1/1)
getJoinCondition (): Expression 100% (1/1)100% (3/3)100% (1/1)
getNestedJoin (): TableFilter 100% (1/1)100% (3/3)100% (1/1)
getSchemaName (): String 100% (1/1)100% (5/5)100% (1/1)
getSelect (): Select 100% (1/1)100% (3/3)100% (1/1)
getSession (): Session 100% (1/1)100% (3/3)100% (1/1)
getSystemColumns (): Column [] 100% (1/1)100% (58/58)100% (10/10)
getTable (): Table 100% (1/1)100% (3/3)100% (1/1)
getTableAlias (): String 100% (1/1)100% (10/10)100% (3/3)
getTableFilter (): TableFilter 100% (1/1)100% (2/2)100% (1/1)
hasInComparisons (): boolean 100% (1/1)100% (25/25)100% (6/6)
hashCode (): int 100% (1/1)100% (3/3)100% (1/1)
isEvaluatable (): boolean 100% (1/1)100% (3/3)100% (1/1)
isJoinOuter (): boolean 100% (1/1)100% (3/3)100% (1/1)
isJoinOuterIndirect (): boolean 100% (1/1)100% (3/3)100% (1/1)
isNaturalJoinColumn (Column): boolean 100% (1/1)100% (12/12)100% (1/1)
isOk (Expression): boolean 100% (1/1)100% (11/11)100% (3/3)
isUsed (): boolean 100% (1/1)100% (3/3)100% (1/1)
lock (Session, boolean, boolean): void 100% (1/1)100% (17/17)100% (4/4)
lockRowAdd (ArrayList): void 100% (1/1)100% (10/10)100% (3/3)
lockRows (ArrayList): void 100% (1/1)100% (41/41)100% (8/8)
optimize (ExpressionColumn, Column): Expression 100% (1/1)100% (2/2)100% (1/1)
optimizeFullCondition (boolean): void 100% (1/1)100% (44/44)100% (7/7)
removeJoin (): void 100% (1/1)100% (4/4)100% (2/2)
removeJoinCondition (): void 100% (1/1)100% (4/4)100% (2/2)
removeUnusableIndexConditions (): void 100% (1/1)100% (25/25)100% (5/5)
reset (): void 100% (1/1)100% (19/19)100% (7/7)
set (Row): void 100% (1/1)100% (7/7)100% (3/3)
setEvaluatable (TableFilter, boolean): void 100% (1/1)100% (40/40)100% (11/11)
setEvaluatable (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setFullCondition (Expression): void 100% (1/1)100% (11/11)100% (4/4)
setIndex (Index): void 100% (1/1)100% (8/8)100% (3/3)
setNullRow (): void 100% (1/1)100% (23/23)100% (6/6)
setSession (Session): void 100% (1/1)100% (4/4)100% (2/2)
setUsed (boolean): void 100% (1/1)100% (4/4)100% (2/2)
startQuery (Session): void 100% (1/1)100% (21/21)100% (7/7)
visit (TableFilter$TableFilterVisitor): void 100% (1/1)100% (19/19)100% (8/8)
     
class TableFilter$1100% (1/1)100% (2/2)100% (9/9)100% (3/3)
TableFilter$1 (TableFilter): void 100% (1/1)100% (6/6)100% (1/1)
accept (TableFilter): void 100% (1/1)100% (3/3)100% (2/2)
     
class TableFilter$2100% (1/1)100% (2/2)100% (15/15)100% (3/3)
TableFilter$2 (TableFilter, Expression): void 100% (1/1)100% (9/9)100% (1/1)
accept (TableFilter): void 100% (1/1)100% (6/6)100% (2/2)
     
class TableFilter$3100% (1/1)100% (2/2)100% (15/15)100% (3/3)
TableFilter$3 (TableFilter, Expression): void 100% (1/1)100% (9/9)100% (1/1)
accept (TableFilter): void 100% (1/1)100% (6/6)100% (2/2)
     
class TableFilter$5100% (1/1)100% (2/2)100% (10/10)100% (3/3)
TableFilter$5 (TableFilter): void 100% (1/1)100% (6/6)100% (1/1)
accept (TableFilter): void 100% (1/1)100% (4/4)100% (2/2)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.table;
7 
8import java.util.ArrayList;
9import org.h2.command.Parser;
10import org.h2.command.dml.Select;
11import org.h2.engine.Right;
12import org.h2.engine.Session;
13import org.h2.engine.SysProperties;
14import org.h2.engine.UndoLogRecord;
15import org.h2.expression.Comparison;
16import org.h2.expression.ConditionAndOr;
17import org.h2.expression.Expression;
18import org.h2.expression.ExpressionColumn;
19import org.h2.index.Index;
20import org.h2.index.IndexCondition;
21import org.h2.index.IndexCursor;
22import org.h2.message.DbException;
23import org.h2.result.Row;
24import org.h2.result.SearchRow;
25import org.h2.result.SortOrder;
26import org.h2.util.New;
27import org.h2.util.StatementBuilder;
28import org.h2.util.StringUtils;
29import org.h2.value.Value;
30import org.h2.value.ValueLong;
31import org.h2.value.ValueNull;
32 
33/**
34 * A table filter represents a table that is used in a query. There is one such
35 * object whenever a table (or view) is used in a query. For example the
36 * following query has 2 table filters: SELECT * FROM TEST T1, TEST T2.
37 */
38public class TableFilter implements ColumnResolver {
39 
40    private static final int BEFORE_FIRST = 0, FOUND = 1, AFTER_LAST = 2,
41            NULL_ROW = 3;
42 
43    /**
44     * Whether this is a direct or indirect (nested) outer join
45     */
46    protected boolean joinOuterIndirect;
47 
48    private Session session;
49 
50    private final Table table;
51    private final Select select;
52    private String alias;
53    private Index index;
54    private int scanCount;
55    private boolean evaluatable;
56 
57    /**
58     * Indicates that this filter is used in the plan.
59     */
60    private boolean used;
61 
62    /**
63     * The filter used to walk through the index.
64     */
65    private final IndexCursor cursor;
66 
67    /**
68     * The index conditions used for direct index lookup (start or end).
69     */
70    private final ArrayList<IndexCondition> indexConditions = New.arrayList();
71 
72    /**
73     * Additional conditions that can't be used for index lookup, but for row
74     * filter for this table (ID=ID, NAME LIKE '%X%')
75     */
76    private Expression filterCondition;
77 
78    /**
79     * The complete join condition.
80     */
81    private Expression joinCondition;
82 
83    private SearchRow currentSearchRow;
84    private Row current;
85    private int state;
86 
87    /**
88     * The joined table (if there is one).
89     */
90    private TableFilter join;
91 
92    /**
93     * Whether this is an outer join.
94     */
95    private boolean joinOuter;
96 
97    /**
98     * The nested joined table (if there is one).
99     */
100    private TableFilter nestedJoin;
101 
102    private ArrayList<Column> naturalJoinColumns;
103    private boolean foundOne;
104    private Expression fullCondition;
105    private final int hashCode;
106 
107    /**
108     * Create a new table filter object.
109     *
110     * @param session the session
111     * @param table the table from where to read data
112     * @param alias the alias name
113     * @param rightsChecked true if rights are already checked
114     * @param select the select statement
115     */
116    public TableFilter(Session session, Table table, String alias,
117            boolean rightsChecked, Select select) {
118        this.session = session;
119        this.table = table;
120        this.alias = alias;
121        this.select = select;
122        this.cursor = new IndexCursor(this);
123        if (!rightsChecked) {
124            session.getUser().checkRight(table, Right.SELECT);
125        }
126        hashCode = session.nextObjectId();
127    }
128 
129    @Override
130    public Select getSelect() {
131        return select;
132    }
133 
134    public Table getTable() {
135        return table;
136    }
137 
138    /**
139     * Lock the table. This will also lock joined tables.
140     *
141     * @param s the session
142     * @param exclusive true if an exclusive lock is required
143     * @param forceLockEvenInMvcc lock even in the MVCC mode
144     */
145    public void lock(Session s, boolean exclusive, boolean forceLockEvenInMvcc) {
146        table.lock(s, exclusive, forceLockEvenInMvcc);
147        if (join != null) {
148            join.lock(s, exclusive, forceLockEvenInMvcc);
149        }
150    }
151 
152    /**
153     * Get the best plan item (index, cost) to use use for the current join
154     * order.
155     *
156     * @param s the session
157     * @param level 1 for the first table in a join, 2 for the second, and so on
158     * @return the best plan item
159     */
160    public PlanItem getBestPlanItem(Session s, int level) {
161        PlanItem item;
162        if (indexConditions.size() == 0) {
163            item = new PlanItem();
164            item.setIndex(table.getScanIndex(s));
165            item.cost = item.getIndex().getCost(s, null, null, null);
166        } else {
167            int len = table.getColumns().length;
168            int[] masks = new int[len];
169            for (IndexCondition condition : indexConditions) {
170                if (condition.isEvaluatable()) {
171                    if (condition.isAlwaysFalse()) {
172                        masks = null;
173                        break;
174                    }
175                    int id = condition.getColumn().getColumnId();
176                    if (id >= 0) {
177                        masks[id] |= condition.getMask(indexConditions);
178                    }
179                }
180            }
181            SortOrder sortOrder = null;
182            if (select != null) {
183                sortOrder = select.getSortOrder();
184            }
185            item = table.getBestPlanItem(s, masks, this, sortOrder);
186            // The more index conditions, the earlier the table.
187            // This is to ensure joins without indexes run quickly:
188            // x (x.a=10); y (x.b=y.b) - see issue 113
189            item.cost -= item.cost * indexConditions.size() / 100 / level;
190        }
191        if (nestedJoin != null) {
192            setEvaluatable(nestedJoin);
193            item.setNestedJoinPlan(nestedJoin.getBestPlanItem(s, level));
194            // TODO optimizer: calculate cost of a join: should use separate
195            // expected row number and lookup cost
196            item.cost += item.cost * item.getNestedJoinPlan().cost;
197        }
198        if (join != null) {
199            setEvaluatable(join);
200            item.setJoinPlan(join.getBestPlanItem(s, level));
201            // TODO optimizer: calculate cost of a join: should use separate
202            // expected row number and lookup cost
203            item.cost += item.cost * item.getJoinPlan().cost;
204        }
205        return item;
206    }
207 
208    private void setEvaluatable(TableFilter join) {
209        if (session.getDatabase().getSettings().nestedJoins) {
210            setEvaluatable(true);
211            return;
212        }
213        // this table filter is now evaluatable - in all sub-joins
214        do {
215            Expression e = join.getJoinCondition();
216            if (e != null) {
217                e.setEvaluatable(this, true);
218            }
219            TableFilter n = join.getNestedJoin();
220            if (n != null) {
221                setEvaluatable(n);
222            }
223            join = join.getJoin();
224        } while (join != null);
225    }
226 
227    /**
228     * Set what plan item (index, cost) to use use.
229     *
230     * @param item the plan item
231     */
232    public void setPlanItem(PlanItem item) {
233        if (item == null) {
234            // invalid plan, most likely because a column wasn't found
235            // this will result in an exception later on
236            return;
237        }
238        setIndex(item.getIndex());
239        if (nestedJoin != null) {
240            if (item.getNestedJoinPlan() != null) {
241                nestedJoin.setPlanItem(item.getNestedJoinPlan());
242            }
243        }
244        if (join != null) {
245            if (item.getJoinPlan() != null) {
246                join.setPlanItem(item.getJoinPlan());
247            }
248        }
249    }
250 
251    /**
252     * Prepare reading rows. This method will remove all index conditions that
253     * can not be used, and optimize the conditions.
254     */
255    public void prepare() {
256        // forget all unused index conditions
257        // the indexConditions list may be modified here
258        for (int i = 0; i < indexConditions.size(); i++) {
259            IndexCondition condition = indexConditions.get(i);
260            if (!condition.isAlwaysFalse()) {
261                Column col = condition.getColumn();
262                if (col.getColumnId() >= 0) {
263                    if (index.getColumnIndex(col) < 0) {
264                        indexConditions.remove(i);
265                        i--;
266                    }
267                }
268            }
269        }
270        if (nestedJoin != null) {
271            if (SysProperties.CHECK && nestedJoin == this) {
272                DbException.throwInternalError("self join");
273            }
274            nestedJoin.prepare();
275        }
276        if (join != null) {
277            if (SysProperties.CHECK && join == this) {
278                DbException.throwInternalError("self join");
279            }
280            join.prepare();
281        }
282        if (filterCondition != null) {
283            filterCondition = filterCondition.optimize(session);
284        }
285        if (joinCondition != null) {
286            joinCondition = joinCondition.optimize(session);
287        }
288    }
289 
290    /**
291     * Start the query. This will reset the scan counts.
292     *
293     * @param s the session
294     */
295    public void startQuery(Session s) {
296        this.session = s;
297        scanCount = 0;
298        if (nestedJoin != null) {
299            nestedJoin.startQuery(s);
300        }
301        if (join != null) {
302            join.startQuery(s);
303        }
304    }
305 
306    /**
307     * Reset to the current position.
308     */
309    public void reset() {
310        if (nestedJoin != null) {
311            nestedJoin.reset();
312        }
313        if (join != null) {
314            join.reset();
315        }
316        state = BEFORE_FIRST;
317        foundOne = false;
318    }
319 
320    /**
321     * Check if there are more rows to read.
322     *
323     * @return true if there are
324     */
325    public boolean next() {
326        if (state == AFTER_LAST) {
327            return false;
328        } else if (state == BEFORE_FIRST) {
329            cursor.find(session, indexConditions);
330            if (!cursor.isAlwaysFalse()) {
331                if (nestedJoin != null) {
332                    nestedJoin.reset();
333                }
334                if (join != null) {
335                    join.reset();
336                }
337            }
338        } else {
339            // state == FOUND || NULL_ROW
340            // the last row was ok - try next row of the join
341            if (join != null && join.next()) {
342                return true;
343            }
344        }
345        while (true) {
346            // go to the next row
347            if (state == NULL_ROW) {
348                break;
349            }
350            if (cursor.isAlwaysFalse()) {
351                state = AFTER_LAST;
352            } else if (nestedJoin != null) {
353                if (state == BEFORE_FIRST) {
354                    state = FOUND;
355                }
356            } else {
357                if ((++scanCount & 4095) == 0) {
358                    checkTimeout();
359                }
360                if (cursor.next()) {
361                    currentSearchRow = cursor.getSearchRow();
362                    current = null;
363                    state = FOUND;
364                } else {
365                    state = AFTER_LAST;
366                }
367            }
368            if (nestedJoin != null && state == FOUND) {
369                if (!nestedJoin.next()) {
370                    state = AFTER_LAST;
371                    if (joinOuter && !foundOne) {
372                        // possibly null row
373                    } else {
374                        continue;
375                    }
376                }
377            }
378            // if no more rows found, try the null row (for outer joins only)
379            if (state == AFTER_LAST) {
380                if (joinOuter && !foundOne) {
381                    setNullRow();
382                } else {
383                    break;
384                }
385            }
386            if (!isOk(filterCondition)) {
387                continue;
388            }
389            boolean joinConditionOk = isOk(joinCondition);
390            if (state == FOUND) {
391                if (joinConditionOk) {
392                    foundOne = true;
393                } else {
394                    continue;
395                }
396            }
397            if (join != null) {
398                join.reset();
399                if (!join.next()) {
400                    continue;
401                }
402            }
403            // check if it's ok
404            if (state == NULL_ROW || joinConditionOk) {
405                return true;
406            }
407        }
408        state = AFTER_LAST;
409        return false;
410    }
411 
412    /**
413     * Set the state of this and all nested tables to the NULL row.
414     */
415    protected void setNullRow() {
416        state = NULL_ROW;
417        current = table.getNullRow();
418        currentSearchRow = current;
419        if (nestedJoin != null) {
420            nestedJoin.visit(new TableFilterVisitor() {
421                @Override
422                public void accept(TableFilter f) {
423                    f.setNullRow();
424                }
425            });
426        }
427    }
428 
429    private void checkTimeout() {
430        session.checkCanceled();
431        // System.out.println(this.alias+ " " + table.getName() + ": " +
432        // scanCount);
433    }
434 
435    private boolean isOk(Expression condition) {
436        if (condition == null) {
437            return true;
438        }
439        return Boolean.TRUE.equals(condition.getBooleanValue(session));
440    }
441 
442    /**
443     * Get the current row.
444     *
445     * @return the current row, or null
446     */
447    public Row get() {
448        if (current == null && currentSearchRow != null) {
449            current = cursor.get();
450        }
451        return current;
452    }
453 
454    /**
455     * Set the current row.
456     *
457     * @param current the current row
458     */
459    public void set(Row current) {
460        // this is currently only used so that check constraints work - to set
461        // the current (new) row
462        this.current = current;
463        this.currentSearchRow = current;
464    }
465 
466    /**
467     * Get the table alias name. If no alias is specified, the table name is
468     * returned.
469     *
470     * @return the alias name
471     */
472    @Override
473    public String getTableAlias() {
474        if (alias != null) {
475            return alias;
476        }
477        return table.getName();
478    }
479 
480    /**
481     * Add an index condition.
482     *
483     * @param condition the index condition
484     */
485    public void addIndexCondition(IndexCondition condition) {
486        indexConditions.add(condition);
487    }
488 
489    /**
490     * Return a list of index condition filtered by a specific column.
491     *
492     * @param column The column of the condition
493     * @return the filtered list
494     */
495    public ArrayList<IndexCondition> getIndexConditionsForColumn(Column column) {
496        ArrayList<IndexCondition> conditions = New.arrayList(indexConditions.size());
497        for (IndexCondition condition: indexConditions) {
498            if (column.equals(condition.getColumn())) {
499                conditions.add(condition);
500            }
501        }
502        return conditions;
503    }
504 
505    /**
506     * Add a filter condition.
507     *
508     * @param condition the condition
509     * @param isJoin if this is in fact a join condition
510     */
511    public void addFilterCondition(Expression condition, boolean isJoin) {
512        if (isJoin) {
513            if (joinCondition == null) {
514                joinCondition = condition;
515            } else {
516                joinCondition = new ConditionAndOr(ConditionAndOr.AND,
517                        joinCondition, condition);
518            }
519        } else {
520            if (filterCondition == null) {
521                filterCondition = condition;
522            } else {
523                filterCondition = new ConditionAndOr(ConditionAndOr.AND,
524                        filterCondition, condition);
525            }
526        }
527    }
528 
529    /**
530     * Add a joined table.
531     *
532     * @param filter the joined table filter
533     * @param outer if this is an outer join
534     * @param nested if this is a nested join
535     * @param on the join condition
536     */
537    public void addJoin(TableFilter filter, boolean outer, boolean nested,
538            final Expression on) {
539        if (on != null) {
540            on.mapColumns(this, 0);
541            if (session.getDatabase().getSettings().nestedJoins) {
542                visit(new TableFilterVisitor() {
543                    @Override
544                    public void accept(TableFilter f) {
545                        on.mapColumns(f, 0);
546                    }
547                });
548                filter.visit(new TableFilterVisitor() {
549                    @Override
550                    public void accept(TableFilter f) {
551                        on.mapColumns(f, 0);
552                    }
553                });
554            }
555        }
556        if (nested && session.getDatabase().getSettings().nestedJoins) {
557            if (nestedJoin != null) {
558                throw DbException.throwInternalError();
559            }
560            nestedJoin = filter;
561            filter.joinOuter = outer;
562            if (outer) {
563                visit(new TableFilterVisitor() {
564                    @Override
565                    public void accept(TableFilter f) {
566                        f.joinOuterIndirect = true;
567                    }
568                });
569            }
570            if (on != null) {
571                filter.mapAndAddFilter(on);
572            }
573        } else {
574            if (join == null) {
575                join = filter;
576                filter.joinOuter = outer;
577                if (session.getDatabase().getSettings().nestedJoins) {
578                    if (outer) {
579                        filter.visit(new TableFilterVisitor() {
580                            @Override
581                            public void accept(TableFilter f) {
582                                f.joinOuterIndirect = true;
583                            }
584                        });
585                    }
586                } else {
587                    if (outer) {
588                        // convert all inner joins on the right hand side to
589                        // outer joins
590                        TableFilter f = filter.join;
591                        while (f != null) {
592                            f.joinOuter = true;
593                            f = f.join;
594                        }
595                    }
596                }
597                if (on != null) {
598                    filter.mapAndAddFilter(on);
599                }
600            } else {
601                join.addJoin(filter, outer, nested, on);
602            }
603        }
604    }
605 
606    /**
607     * Map the columns and add the join condition.
608     *
609     * @param on the condition
610     */
611    public void mapAndAddFilter(Expression on) {
612        on.mapColumns(this, 0);
613        addFilterCondition(on, true);
614        on.createIndexConditions(session, this);
615        if (nestedJoin != null) {
616            on.mapColumns(nestedJoin, 0);
617            on.createIndexConditions(session, nestedJoin);
618        }
619        if (join != null) {
620            join.mapAndAddFilter(on);
621        }
622    }
623 
624    public TableFilter getJoin() {
625        return join;
626    }
627 
628    /**
629     * Whether this is an outer joined table.
630     *
631     * @return true if it is
632     */
633    public boolean isJoinOuter() {
634        return joinOuter;
635    }
636 
637    /**
638     * Whether this is indirectly an outer joined table (nested within an inner
639     * join).
640     *
641     * @return true if it is
642     */
643    public boolean isJoinOuterIndirect() {
644        return joinOuterIndirect;
645    }
646 
647    /**
648     * Get the query execution plan text to use for this table filter.
649     *
650     * @param isJoin if this is a joined table
651     * @return the SQL statement snippet
652     */
653    public String getPlanSQL(boolean isJoin) {
654        StringBuilder buff = new StringBuilder();
655        if (isJoin) {
656            if (joinOuter) {
657                buff.append("LEFT OUTER JOIN ");
658            } else {
659                buff.append("INNER JOIN ");
660            }
661        }
662        if (nestedJoin != null) {
663            StringBuffer buffNested = new StringBuffer();
664            TableFilter n = nestedJoin;
665            do {
666                buffNested.append(n.getPlanSQL(n != nestedJoin));
667                buffNested.append('\n');
668                n = n.getJoin();
669            } while (n != null);
670            String nested = buffNested.toString();
671            boolean enclose = !nested.startsWith("(");
672            if (enclose) {
673                buff.append("(\n");
674            }
675            buff.append(StringUtils.indent(nested, 4, false));
676            if (enclose) {
677                buff.append(')');
678            }
679            if (isJoin) {
680                buff.append(" ON ");
681                if (joinCondition == null) {
682                    // need to have a ON expression,
683                    // otherwise the nesting is unclear
684                    buff.append("1=1");
685                } else {
686                    buff.append(StringUtils.unEnclose(joinCondition.getSQL()));
687                }
688            }
689            return buff.toString();
690        }
691        buff.append(table.getSQL());
692        if (alias != null) {
693            buff.append(' ').append(Parser.quoteIdentifier(alias));
694        }
695        if (index != null) {
696            buff.append('\n');
697            StatementBuilder planBuff = new StatementBuilder();
698            planBuff.append(index.getPlanSQL());
699            if (indexConditions.size() > 0) {
700                planBuff.append(": ");
701                for (IndexCondition condition : indexConditions) {
702                    planBuff.appendExceptFirst("\n    AND ");
703                    planBuff.append(condition.getSQL());
704                }
705            }
706            String plan = StringUtils.quoteRemarkSQL(planBuff.toString());
707            if (plan.indexOf('\n') >= 0) {
708                plan += "\n";
709            }
710            buff.append(StringUtils.indent("/* " + plan + " */", 4, false));
711        }
712        if (isJoin) {
713            buff.append("\n    ON ");
714            if (joinCondition == null) {
715                // need to have a ON expression, otherwise the nesting is
716                // unclear
717                buff.append("1=1");
718            } else {
719                buff.append(StringUtils.unEnclose(joinCondition.getSQL()));
720            }
721        }
722        if (filterCondition != null) {
723            buff.append('\n');
724            String condition = StringUtils.unEnclose(filterCondition.getSQL());
725            condition = "/* WHERE " + StringUtils.quoteRemarkSQL(condition) + "\n*/";
726            buff.append(StringUtils.indent(condition, 4, false));
727        }
728        if (scanCount > 0) {
729            buff.append("\n    /* scanCount: ").append(scanCount).append(" */");
730        }
731        return buff.toString();
732    }
733 
734    /**
735     * Remove all index conditions that are not used by the current index.
736     */
737    void removeUnusableIndexConditions() {
738        // the indexConditions list may be modified here
739        for (int i = 0; i < indexConditions.size(); i++) {
740            IndexCondition cond = indexConditions.get(i);
741            if (!cond.isEvaluatable()) {
742                indexConditions.remove(i--);
743            }
744        }
745    }
746 
747    public Index getIndex() {
748        return index;
749    }
750 
751    public void setIndex(Index index) {
752        this.index = index;
753        cursor.setIndex(index);
754    }
755 
756    public void setUsed(boolean used) {
757        this.used = used;
758    }
759 
760    public boolean isUsed() {
761        return used;
762    }
763 
764    /**
765     * Set the session of this table filter.
766     *
767     * @param session the new session
768     */
769    void setSession(Session session) {
770        this.session = session;
771    }
772 
773    /**
774     * Remove the joined table
775     */
776    public void removeJoin() {
777        this.join = null;
778    }
779 
780    public Expression getJoinCondition() {
781        return joinCondition;
782    }
783 
784    /**
785     * Remove the join condition.
786     */
787    public void removeJoinCondition() {
788        this.joinCondition = null;
789    }
790 
791    public Expression getFilterCondition() {
792        return filterCondition;
793    }
794 
795    /**
796     * Remove the filter condition.
797     */
798    public void removeFilterCondition() {
799        this.filterCondition = null;
800    }
801 
802    public void setFullCondition(Expression condition) {
803        this.fullCondition = condition;
804        if (join != null) {
805            join.setFullCondition(condition);
806        }
807    }
808 
809    /**
810     * Optimize the full condition. This will add the full condition to the
811     * filter condition.
812     *
813     * @param fromOuterJoin if this method was called from an outer joined table
814     */
815    void optimizeFullCondition(boolean fromOuterJoin) {
816        if (fullCondition != null) {
817            fullCondition.addFilterConditions(this, fromOuterJoin || joinOuter);
818            if (nestedJoin != null) {
819                nestedJoin.optimizeFullCondition(fromOuterJoin || joinOuter);
820            }
821            if (join != null) {
822                join.optimizeFullCondition(fromOuterJoin || joinOuter);
823            }
824        }
825    }
826 
827    /**
828     * Update the filter and join conditions of this and all joined tables with
829     * the information that the given table filter and all nested filter can now
830     * return rows or not.
831     *
832     * @param filter the table filter
833     * @param b the new flag
834     */
835    public void setEvaluatable(TableFilter filter, boolean b) {
836        filter.setEvaluatable(b);
837        if (filterCondition != null) {
838            filterCondition.setEvaluatable(filter, b);
839        }
840        if (joinCondition != null) {
841            joinCondition.setEvaluatable(filter, b);
842        }
843        if (nestedJoin != null) {
844            // don't enable / disable the nested join filters
845            // if enabling a filter in a joined filter
846            if (this == filter) {
847                nestedJoin.setEvaluatable(nestedJoin, b);
848            }
849        }
850        if (join != null) {
851            join.setEvaluatable(filter, b);
852        }
853    }
854 
855    public void setEvaluatable(boolean evaluatable) {
856        this.evaluatable = evaluatable;
857    }
858 
859    @Override
860    public String getSchemaName() {
861        return table.getSchema().getName();
862    }
863 
864    @Override
865    public Column[] getColumns() {
866        return table.getColumns();
867    }
868 
869    /**
870     * Get the system columns that this table understands. This is used for
871     * compatibility with other databases. The columns are only returned if the
872     * current mode supports system columns.
873     *
874     * @return the system columns
875     */
876    @Override
877    public Column[] getSystemColumns() {
878        if (!session.getDatabase().getMode().systemColumns) {
879            return null;
880        }
881        Column[] sys = new Column[3];
882        sys[0] = new Column("oid", Value.INT);
883        sys[0].setTable(table, 0);
884        sys[1] = new Column("ctid", Value.STRING);
885        sys[1].setTable(table, 0);
886        sys[2] = new Column("CTID", Value.STRING);
887        sys[2].setTable(table, 0);
888        return sys;
889    }
890 
891    @Override
892    public Column getRowIdColumn() {
893        if (session.getDatabase().getSettings().rowId) {
894            return table.getRowIdColumn();
895        }
896        return null;
897    }
898 
899    @Override
900    public Value getValue(Column column) {
901        if (currentSearchRow == null) {
902            return null;
903        }
904        int columnId = column.getColumnId();
905        if (columnId == -1) {
906            return ValueLong.get(currentSearchRow.getKey());
907        }
908        if (current == null) {
909            Value v = currentSearchRow.getValue(columnId);
910            if (v != null) {
911                return v;
912            }
913            current = cursor.get();
914            if (current == null) {
915                return ValueNull.INSTANCE;
916            }
917        }
918        return current.getValue(columnId);
919    }
920 
921    @Override
922    public TableFilter getTableFilter() {
923        return this;
924    }
925 
926    public void setAlias(String alias) {
927        this.alias = alias;
928    }
929 
930    @Override
931    public Expression optimize(ExpressionColumn expressionColumn, Column column) {
932        return expressionColumn;
933    }
934 
935    @Override
936    public String toString() {
937        return alias != null ? alias : table.toString();
938    }
939 
940    /**
941     * Add a column to the natural join key column list.
942     *
943     * @param c the column to add
944     */
945    public void addNaturalJoinColumn(Column c) {
946        if (naturalJoinColumns == null) {
947            naturalJoinColumns = New.arrayList();
948        }
949        naturalJoinColumns.add(c);
950    }
951 
952    /**
953     * Check if the given column is a natural join column.
954     *
955     * @param c the column to check
956     * @return true if this is a joined natural join column
957     */
958    public boolean isNaturalJoinColumn(Column c) {
959        return naturalJoinColumns != null && naturalJoinColumns.contains(c);
960    }
961 
962    @Override
963    public int hashCode() {
964        return hashCode;
965    }
966 
967    /**
968     * Are there any index conditions that involve IN(...).
969     *
970     * @return whether there are IN(...) comparisons
971     */
972    public boolean hasInComparisons() {
973        for (IndexCondition cond : indexConditions) {
974            int compareType = cond.getCompareType();
975            if (compareType == Comparison.IN_QUERY || compareType == Comparison.IN_LIST) {
976                return true;
977            }
978        }
979        return false;
980    }
981 
982    /**
983     * Add the current row to the array, if there is a current row.
984     *
985     * @param rows the rows to lock
986     */
987    public void lockRowAdd(ArrayList<Row> rows) {
988        if (state == FOUND) {
989            rows.add(get());
990        }
991    }
992 
993    /**
994     * Lock the given rows.
995     *
996     * @param forUpdateRows the rows to lock
997     */
998    public void lockRows(ArrayList<Row> forUpdateRows) {
999        for (Row row : forUpdateRows) {
1000            Row newRow = row.getCopy();
1001            table.removeRow(session, row);
1002            session.log(table, UndoLogRecord.DELETE, row);
1003            table.addRow(session, newRow);
1004            session.log(table, UndoLogRecord.INSERT, newRow);
1005        }
1006    }
1007 
1008    public TableFilter getNestedJoin() {
1009        return nestedJoin;
1010    }
1011 
1012    /**
1013     * Visit this and all joined or nested table filters.
1014     *
1015     * @param visitor the visitor
1016     */
1017    public void visit(TableFilterVisitor visitor) {
1018        TableFilter f = this;
1019        do {
1020            visitor.accept(f);
1021            TableFilter n = f.nestedJoin;
1022            if (n != null) {
1023                n.visit(visitor);
1024            }
1025            f = f.join;
1026        } while (f != null);
1027    }
1028 
1029    public boolean isEvaluatable() {
1030        return evaluatable;
1031    }
1032 
1033    public Session getSession() {
1034        return session;
1035    }
1036 
1037    /**
1038     * A visitor for table filters.
1039     */
1040    public interface TableFilterVisitor {
1041 
1042        /**
1043         * This method is called for each nested or joined table filter.
1044         *
1045         * @param f the filter
1046         */
1047        void accept(TableFilter f);
1048    }
1049 
1050}

[all classes][org.h2.table]
EMMA 2.0.5312 (C) Vladimir Roubtsov