SelectListColumnResolver(Select select) {
   this.select = select;
   int columnCount = select.getColumnCount();
   columns = new Column[columnCount];
   expressions = new Expression[columnCount];
   ArrayList<Expression> columnList = select.getExpressions();
   for (int i = 0; i < columnCount; i++) {
     Expression expr = columnList.get(i);
     Column column = new Column(expr.getAlias(), Value.NULL);
     column.setTable(null, i);
     columns[i] = column;
     expressions[i] = expr.getNonAliasExpression();
   }
 }
예제 #2
0
 public void addGlobalCondition(Parameter param, int columnId, int comparisonType) {
   addParameter(param);
   Expression comp;
   Expression col = expressions.get(columnId);
   col = col.getNonAliasExpression();
   if (col.isEverything(ExpressionVisitor.QUERY_COMPARABLE_VISITOR)) {
     comp = new Comparison(session, comparisonType, col, param);
   } else {
     // this condition will always evaluate to true, but need to
     // add the parameter, so it can be set later
     comp = new Comparison(session, Comparison.EQUAL_NULL_SAFE, param, param);
   }
   comp = comp.optimize(session);
   boolean addToCondition = true;
   if (isGroupQuery) {
     addToCondition = false;
     for (int i = 0; groupIndex != null && i < groupIndex.length; i++) {
       if (groupIndex[i] == columnId) {
         addToCondition = true;
         break;
       }
     }
     if (!addToCondition) {
       if (havingIndex >= 0) {
         having = expressions.get(havingIndex);
       }
       if (having == null) {
         having = comp;
       } else {
         having = new ConditionAndOr(ConditionAndOr.AND, having, comp);
       }
     }
   }
   if (addToCondition) {
     if (condition == null) {
       condition = comp;
     } else {
       condition = new ConditionAndOr(ConditionAndOr.AND, condition, comp);
     }
   }
 }
예제 #3
0
 /**
  * Initialize the order by list. This call may extend the expressions list.
  *
  * @param session the session
  * @param expressions the select list expressions
  * @param expressionSQL the select list SQL snippets
  * @param orderList the order by list
  * @param visible the number of visible columns in the select list
  * @param mustBeInResult all order by expressions must be in the select list
  * @param filters the table filters
  */
 static void initOrder(
     Session session,
     ArrayList<Expression> expressions,
     ArrayList<String> expressionSQL,
     ArrayList<SelectOrderBy> orderList,
     int visible,
     boolean mustBeInResult,
     ArrayList<TableFilter> filters) {
   Database db = session.getDatabase();
   for (SelectOrderBy o : orderList) {
     Expression e = o.expression;
     if (e == null) {
       continue;
     }
     // special case: SELECT 1 AS A FROM DUAL ORDER BY A
     // (oracle supports it, but only in order by, not in group by and
     // not in having):
     // SELECT 1 AS A FROM DUAL ORDER BY -A
     boolean isAlias = false;
     int idx = expressions.size();
     if (e instanceof ExpressionColumn) {
       // order by expression
       ExpressionColumn exprCol = (ExpressionColumn) e;
       String tableAlias = exprCol.getOriginalTableAliasName();
       String col = exprCol.getOriginalColumnName();
       for (int j = 0; j < visible; j++) {
         boolean found = false;
         Expression ec = expressions.get(j);
         if (ec instanceof ExpressionColumn) {
           // select expression
           ExpressionColumn c = (ExpressionColumn) ec;
           found = db.equalsIdentifiers(col, c.getColumnName());
           if (found && tableAlias != null) {
             String ca = c.getOriginalTableAliasName();
             if (ca == null) {
               found = false;
               if (filters != null) {
                 // select id from test order by test.id
                 for (int i = 0, size = filters.size(); i < size; i++) {
                   TableFilter f = filters.get(i);
                   if (db.equalsIdentifiers(f.getTableAlias(), tableAlias)) {
                     found = true;
                     break;
                   }
                 }
               }
             } else {
               found = db.equalsIdentifiers(ca, tableAlias);
             }
           }
         } else if (!(ec instanceof Alias)) {
           continue;
         } else if (tableAlias == null && db.equalsIdentifiers(col, ec.getAlias())) {
           found = true;
         } else {
           Expression ec2 = ec.getNonAliasExpression();
           if (ec2 instanceof ExpressionColumn) {
             ExpressionColumn c2 = (ExpressionColumn) ec2;
             String ta = exprCol.getSQL();
             String tb = c2.getSQL();
             String s2 = c2.getColumnName();
             found = db.equalsIdentifiers(col, s2);
             if (!db.equalsIdentifiers(ta, tb)) {
               found = false;
             }
           }
         }
         if (found) {
           idx = j;
           isAlias = true;
           break;
         }
       }
     } else {
       String s = e.getSQL();
       if (expressionSQL != null) {
         for (int j = 0, size = expressionSQL.size(); j < size; j++) {
           String s2 = expressionSQL.get(j);
           if (db.equalsIdentifiers(s2, s)) {
             idx = j;
             isAlias = true;
             break;
           }
         }
       }
     }
     if (!isAlias) {
       if (mustBeInResult) {
         throw DbException.get(ErrorCode.ORDER_BY_NOT_IN_RESULT, e.getSQL());
       }
       expressions.add(e);
       String sql = e.getSQL();
       expressionSQL.add(sql);
     }
     o.columnIndexExpr = ValueExpression.get(ValueInt.get(idx + 1));
     Expression expr = expressions.get(idx).getNonAliasExpression();
     o.expression = expr;
   }
 }
예제 #4
0
 public String getPlanSQL() {
   // can not use the field sqlStatement because the parameter
   // indexes may be incorrect: ? may be in fact ?2 for a subquery
   // but indexes may be set manually as well
   Expression[] exprList = expressions.toArray(new Expression[expressions.size()]);
   StatementBuilder buff = new StatementBuilder("SELECT");
   if (distinct) {
     buff.append(" DISTINCT");
   }
   for (int i = 0; i < visibleColumnCount; i++) {
     buff.appendExceptFirst(",");
     buff.append('\n');
     buff.append(StringUtils.indent(exprList[i].getSQL(), 4, false));
   }
   buff.append("\nFROM ");
   TableFilter filter = topTableFilter;
   if (filter != null) {
     buff.resetCount();
     int i = 0;
     do {
       buff.appendExceptFirst("\n");
       buff.append(filter.getPlanSQL(i++ > 0));
       filter = filter.getJoin();
     } while (filter != null);
   } else {
     buff.resetCount();
     int i = 0;
     for (TableFilter f : topFilters) {
       do {
         buff.appendExceptFirst("\n");
         buff.append(f.getPlanSQL(i++ > 0));
         f = f.getJoin();
       } while (f != null);
     }
   }
   if (condition != null) {
     buff.append("\nWHERE ").append(StringUtils.unEnclose(condition.getSQL()));
   }
   if (groupIndex != null) {
     buff.append("\nGROUP BY ");
     buff.resetCount();
     for (int gi : groupIndex) {
       Expression g = exprList[gi];
       g = g.getNonAliasExpression();
       buff.appendExceptFirst(", ");
       buff.append(StringUtils.unEnclose(g.getSQL()));
     }
   }
   if (group != null) {
     buff.append("\nGROUP BY ");
     buff.resetCount();
     for (Expression g : group) {
       buff.appendExceptFirst(", ");
       buff.append(StringUtils.unEnclose(g.getSQL()));
     }
   }
   if (having != null) {
     // could be set in addGlobalCondition
     // in this case the query is not run directly, just getPlanSQL is
     // called
     Expression h = having;
     buff.append("\nHAVING ").append(StringUtils.unEnclose(h.getSQL()));
   } else if (havingIndex >= 0) {
     Expression h = exprList[havingIndex];
     buff.append("\nHAVING ").append(StringUtils.unEnclose(h.getSQL()));
   }
   if (sort != null) {
     buff.append("\nORDER BY ").append(sort.getSQL(exprList, visibleColumnCount));
   }
   if (orderList != null) {
     buff.append("\nORDER BY ");
     buff.resetCount();
     for (SelectOrderBy o : orderList) {
       buff.appendExceptFirst(", ");
       buff.append(StringUtils.unEnclose(o.getSQL()));
     }
   }
   if (limitExpr != null) {
     buff.append("\nLIMIT ").append(StringUtils.unEnclose(limitExpr.getSQL()));
     if (offsetExpr != null) {
       buff.append(" OFFSET ").append(StringUtils.unEnclose(offsetExpr.getSQL()));
     }
   }
   if (sampleSize != 0) {
     buff.append("\nSAMPLE_SIZE ").append(sampleSize);
   }
   if (isForUpdate) {
     buff.append("\nFOR UPDATE");
   }
   if (isQuickAggregateQuery) {
     buff.append("\n/* direct lookup */");
   }
   if (isDistinctQuery) {
     buff.append("\n/* distinct */");
   }
   if (sortUsingIndex) {
     buff.append("\n/* index sorted */");
   }
   if (isGroupQuery) {
     if (isGroupSortedQuery) {
       buff.append("\n/* group sorted */");
     }
   }
   // buff.append("\n/* cost: " + cost + " */");
   return buff.toString();
 }
예제 #5
0
 public void prepare() {
   if (isPrepared) {
     // sometimes a subquery is prepared twice (CREATE TABLE AS SELECT)
     return;
   }
   if (SysProperties.CHECK && !checkInit) {
     DbException.throwInternalError("not initialized");
   }
   if (orderList != null) {
     sort = prepareOrder(orderList, expressions.size());
     orderList = null;
   }
   for (int i = 0; i < expressions.size(); i++) {
     Expression e = expressions.get(i);
     expressions.set(i, e.optimize(session));
   }
   if (condition != null) {
     condition = condition.optimize(session);
     for (TableFilter f : filters) {
       // outer joins: must not add index conditions such as
       // "c is null" - example:
       // create table parent(p int primary key) as select 1;
       // create table child(c int primary key, pc int);
       // insert into child values(2, 1);
       // select p, c from parent
       // left outer join child on p = pc where c is null;
       if (!f.isJoinOuter() && !f.isJoinOuterIndirect()) {
         condition.createIndexConditions(session, f);
       }
     }
   }
   if (isGroupQuery && groupIndex == null && havingIndex < 0 && filters.size() == 1) {
     if (condition == null) {
       Table t = filters.get(0).getTable();
       ExpressionVisitor optimizable = ExpressionVisitor.getOptimizableVisitor(t);
       isQuickAggregateQuery = isEverything(optimizable);
     }
   }
   cost = preparePlan();
   if (distinct
       && session.getDatabase().getSettings().optimizeDistinct
       && !isGroupQuery
       && filters.size() == 1
       && expressions.size() == 1
       && condition == null) {
     Expression expr = expressions.get(0);
     expr = expr.getNonAliasExpression();
     if (expr instanceof ExpressionColumn) {
       Column column = ((ExpressionColumn) expr).getColumn();
       int selectivity = column.getSelectivity();
       Index columnIndex = topTableFilter.getTable().getIndexForColumn(column);
       if (columnIndex != null
           && selectivity != Constants.SELECTIVITY_DEFAULT
           && selectivity < 20) {
         // the first column must be ascending
         boolean ascending = columnIndex.getIndexColumns()[0].sortType == SortOrder.ASCENDING;
         Index current = topTableFilter.getIndex();
         // if another index is faster
         if (columnIndex.canFindNext()
             && ascending
             && (current == null || current.getIndexType().isScan() || columnIndex == current)) {
           IndexType type = columnIndex.getIndexType();
           // hash indexes don't work, and unique single column indexes don't work
           if (!type.isHash() && (!type.isUnique() || columnIndex.getColumns().length > 1)) {
             topTableFilter.setIndex(columnIndex);
             isDistinctQuery = true;
           }
         }
       }
     }
   }
   if (sort != null && !isQuickAggregateQuery && !isGroupQuery) {
     Index index = getSortIndex();
     if (index != null) {
       Index current = topTableFilter.getIndex();
       if (current.getIndexType().isScan() || current == index) {
         topTableFilter.setIndex(index);
         if (!topTableFilter.hasInComparisons()) {
           // in(select ...) and in(1,2,3) my return the key in another order
           sortUsingIndex = true;
         }
       } else if (index.getIndexColumns().length >= current.getIndexColumns().length) {
         IndexColumn[] sortColumns = index.getIndexColumns();
         IndexColumn[] currentColumns = current.getIndexColumns();
         boolean swapIndex = false;
         for (int i = 0; i < currentColumns.length; i++) {
           if (sortColumns[i].column != currentColumns[i].column) {
             swapIndex = false;
             break;
           }
           if (sortColumns[i].sortType != currentColumns[i].sortType) {
             swapIndex = true;
           }
         }
         if (swapIndex) {
           topTableFilter.setIndex(index);
           sortUsingIndex = true;
         }
       }
     }
   }
   if (!isQuickAggregateQuery && isGroupQuery && getGroupByExpressionCount() > 0) {
     Index index = getGroupSortedIndex();
     Index current = topTableFilter.getIndex();
     if (index != null && (current.getIndexType().isScan() || current == index)) {
       topTableFilter.setIndex(index);
       isGroupSortedQuery = true;
     }
   }
   expressionArray = new Expression[expressions.size()];
   expressions.toArray(expressionArray);
   isPrepared = true;
 }
예제 #6
0
  public void init() {
    if (SysProperties.CHECK && checkInit) {
      DbException.throwInternalError();
    }
    expandColumnList();
    visibleColumnCount = expressions.size();
    ArrayList<String> expressionSQL;
    if (orderList != null || group != null) {
      expressionSQL = New.arrayList();
      for (int i = 0; i < visibleColumnCount; i++) {
        Expression expr = expressions.get(i);
        expr = expr.getNonAliasExpression();
        String sql = expr.getSQL();
        expressionSQL.add(sql);
      }
    } else {
      expressionSQL = null;
    }
    if (orderList != null) {
      initOrder(
          session, expressions, expressionSQL, orderList, visibleColumnCount, distinct, filters);
    }
    distinctColumnCount = expressions.size();
    if (having != null) {
      expressions.add(having);
      havingIndex = expressions.size() - 1;
      having = null;
    } else {
      havingIndex = -1;
    }

    Database db = session.getDatabase();

    // first the select list (visible columns),
    // then 'ORDER BY' expressions,
    // then 'HAVING' expressions,
    // and 'GROUP BY' expressions at the end
    if (group != null) {
      int size = group.size();
      int expSize = expressionSQL.size();
      groupIndex = new int[size];
      for (int i = 0; i < size; i++) {
        Expression expr = group.get(i);
        String sql = expr.getSQL();
        int found = -1;
        for (int j = 0; j < expSize; j++) {
          String s2 = expressionSQL.get(j);
          if (db.equalsIdentifiers(s2, sql)) {
            found = j;
            break;
          }
        }
        if (found < 0) {
          // special case: GROUP BY a column alias
          for (int j = 0; j < expSize; j++) {
            Expression e = expressions.get(j);
            if (db.equalsIdentifiers(sql, e.getAlias())) {
              found = j;
              break;
            }
          }
        }
        if (found < 0) {
          int index = expressions.size();
          groupIndex[i] = index;
          expressions.add(expr);
        } else {
          groupIndex[i] = found;
        }
      }
      groupByExpression = new boolean[expressions.size()];
      for (int gi : groupIndex) {
        groupByExpression[gi] = true;
      }
      group = null;
    }
    // map columns in select list and condition
    for (TableFilter f : filters) {
      for (Expression expr : expressions) {
        expr.mapColumns(f, 0);
      }
      if (condition != null) {
        condition.mapColumns(f, 0);
      }
    }
    if (havingIndex >= 0) {
      Expression expr = expressions.get(havingIndex);
      SelectListColumnResolver res = new SelectListColumnResolver(this);
      expr.mapColumns(res, 0);
    }
    checkInit = true;
  }
예제 #7
0
 /**
  * Get the index that matches the ORDER BY list, if one exists. This is to avoid running a
  * separate ORDER BY if an index can be used. This is specially important for large result sets,
  * if only the first few rows are important (LIMIT is used)
  *
  * @return the index if one is found
  */
 private Index getSortIndex() {
   if (sort == null) {
     return null;
   }
   ArrayList<Column> sortColumns = New.arrayList();
   for (int idx : sort.getIndexes()) {
     if (idx < 0 || idx >= expressions.size()) {
       throw DbException.getInvalidValueException("ORDER BY", idx + 1);
     }
     Expression expr = expressions.get(idx);
     expr = expr.getNonAliasExpression();
     if (expr.isConstant()) {
       continue;
     }
     if (!(expr instanceof ExpressionColumn)) {
       return null;
     }
     ExpressionColumn exprCol = (ExpressionColumn) expr;
     if (exprCol.getTableFilter() != topTableFilter) {
       return null;
     }
     sortColumns.add(exprCol.getColumn());
   }
   Column[] sortCols = sortColumns.toArray(new Column[sortColumns.size()]);
   int[] sortTypes = sort.getSortTypes();
   if (sortCols.length == 0) {
     // sort just on constants - can use scan index
     return topTableFilter.getTable().getScanIndex(session);
   }
   ArrayList<Index> list = topTableFilter.getTable().getIndexes();
   if (list != null) {
     for (int i = 0, size = list.size(); i < size; i++) {
       Index index = list.get(i);
       if (index.getCreateSQL() == null) {
         // can't use the scan index
         continue;
       }
       if (index.getIndexType().isHash()) {
         continue;
       }
       IndexColumn[] indexCols = index.getIndexColumns();
       if (indexCols.length < sortCols.length) {
         continue;
       }
       boolean ok = true;
       for (int j = 0; j < sortCols.length; j++) {
         // the index and the sort order must start
         // with the exact same columns
         IndexColumn idxCol = indexCols[j];
         Column sortCol = sortCols[j];
         if (idxCol.column != sortCol) {
           ok = false;
           break;
         }
         if (idxCol.sortType != sortTypes[j]) {
           // NULL FIRST for ascending and NULLS LAST
           // for descending would actually match the default
           ok = false;
           break;
         }
       }
       if (ok) {
         return index;
       }
     }
   }
   if (sortCols.length == 1 && sortCols[0].getColumnId() == -1) {
     // special case: order by _ROWID_
     Index index = topTableFilter.getTable().getScanIndex(session);
     if (index.isRowIdIndex()) {
       return index;
     }
   }
   return null;
 }