/* * 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.ignite.internal.processors.query.h2.sql; import java.util.ArrayList; import java.util.List; import org.h2.util.StatementBuilder; import org.h2.util.StringUtils; /** * Plain SELECT query. */ public class GridSqlSelect extends GridSqlQuery { /** */ public static final int FROM_CHILD = 2; /** */ public static final int WHERE_CHILD = 3; /** */ private static final int COLS_CHILD = 4; /** */ private List<GridSqlAst> cols = new ArrayList<>(); /** */ private int visibleCols; /** */ private boolean distinct; /** */ private int[] grpCols; /** */ private GridSqlAst from; /** */ private GridSqlAst where; /** */ private int havingCol = -1; /** * @param colIdx Column index as for {@link #column(int)}. * @return Child index for {@link #child(int)}. */ public static int childIndexForColumn(int colIdx) { return colIdx + COLS_CHILD; } /** {@inheritDoc} */ @Override public int size() { return 4 + cols.size(); // + FROM + WHERE + OFFSET + LIMIT } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <E extends GridSqlAst> E child(int childIdx) { if (childIdx < FROM_CHILD) return super.child(childIdx); switch (childIdx) { case FROM_CHILD: return maskNull(from, GridSqlPlaceholder.EMPTY); case WHERE_CHILD: return maskNull(where, GridSqlConst.TRUE); default: return (E)cols.get(childIdx - COLS_CHILD); } } /** {@inheritDoc} */ @Override public <E extends GridSqlAst> void child(int childIdx, E child) { if (childIdx < FROM_CHILD) { super.child(childIdx, child); return; } switch (childIdx) { case FROM_CHILD: from = child; break; case WHERE_CHILD: where = child; break; default: cols.set(childIdx - COLS_CHILD, child); } } /** {@inheritDoc} */ @Override public int visibleColumns() { return visibleCols; } /** * @return Number of columns is select including invisible ones. */ public int allColumns() { return cols.size(); } /** {@inheritDoc} */ @Override protected GridSqlAst column(int col) { return cols.get(col); } /** {@inheritDoc} */ @Override public String getSQL() { StatementBuilder buff = new StatementBuilder(explain() ? "EXPLAIN SELECT" : "SELECT"); if (distinct) buff.append(" DISTINCT"); for (GridSqlAst expression : columns(true)) { buff.appendExceptFirst(","); buff.append('\n'); buff.append(expression.getSQL()); } if (from != null) buff.append("\nFROM ").append(from.getSQL()); if (where != null) buff.append("\nWHERE ").append(StringUtils.unEnclose(where.getSQL())); if (grpCols != null) { buff.append("\nGROUP BY "); buff.resetCount(); for (int grpCol : grpCols) { buff.appendExceptFirst(", "); addAlias(buff, cols.get(grpCol)); } } if (havingCol >= 0) { buff.append("\nHAVING "); addAlias(buff, cols.get(havingCol)); } getSortLimitSQL(buff); return buff.toString(); } /** * @return {@code True} if this simple SQL query like 'SELECT A, B, C from SOME_TABLE' without any conditions * and expressions. */ @Override public boolean simpleQuery() { boolean simple = !distinct && from instanceof GridSqlTable && where == null && grpCols == null && havingCol < 0 && sort.isEmpty() && limit() == null && offset() == null; if (simple) { for (GridSqlAst expression : columns(true)) { if (expression instanceof GridSqlAlias) expression = expression.child(); if (!(expression instanceof GridSqlColumn)) return false; } } return simple; } /** * @param buff Statement builder. * @param exp Alias expression. */ private static void addAlias(StatementBuilder buff, GridSqlAst exp) { exp = GridSqlAlias.unwrap(exp); buff.append(StringUtils.unEnclose(exp.getSQL())); } /** * @param visibleOnly If only visible expressions needed. * @return Select clause expressions. */ public List<GridSqlAst> columns(boolean visibleOnly) { assert visibleCols <= cols.size(); return visibleOnly && visibleCols != cols.size() ? cols.subList(0, visibleCols) : cols; } /** * Clears select expressions list. * @return {@code this}. */ public GridSqlSelect clearColumns() { visibleCols = 0; cols = new ArrayList<>(); return this; } /** * @param expression Expression. * @param visible Expression is visible in select phrase. * @return {@code this}. */ public GridSqlSelect addColumn(GridSqlAst expression, boolean visible) { if (expression == null) throw new NullPointerException(); if (visible) { if (visibleCols != cols.size()) throw new IllegalStateException("Already started adding invisible columns."); visibleCols++; } cols.add(expression); return this; } /** * @param colIdx Column index. * @param expression Expression. * @return {@code this}. */ public GridSqlSelect setColumn(int colIdx, GridSqlAst expression) { if (expression == null) throw new NullPointerException(); cols.set(colIdx, expression); return this; } /** * @return Group columns. */ public int[] groupColumns() { return grpCols; } /** * @param grpCols Group columns. * @return {@code this}. */ public GridSqlSelect groupColumns(int[] grpCols) { this.grpCols = grpCols; return this; } /** * @return Tables. */ public GridSqlAst from() { return from; } /** * @param from From element. * @return {@code this}. */ public GridSqlSelect from(GridSqlAst from) { this.from = from; return this; } /** * @return Distinct. */ public boolean distinct() { return distinct; } /** * @param distinct New distinct. */ public void distinct(boolean distinct) { this.distinct = distinct; } /** * @return Where. */ public GridSqlAst where() { return where; } /** * @param where New where. * @return {@code this}. */ public GridSqlSelect where(GridSqlAst where) { this.where = where; return this; } /** * @param cond Adds new WHERE condition using AND operator. * @return {@code this}. */ public GridSqlSelect whereAnd(GridSqlAst cond) { if (cond == null) throw new NullPointerException(); GridSqlAst old = where(); where(old == null ? cond : new GridSqlOperation(GridSqlOperationType.AND, old, cond)); return this; } /** * @return Having. */ public GridSqlAst having() { return havingCol >= 0 ? column(havingCol) : null; } /** * @param col Index of HAVING column. * @return {@code this}. */ public GridSqlSelect havingColumn(int col) { assert col >= -1 : col; havingCol = col; return this; } /** * @return Index of HAVING column. */ public int havingColumn() { return havingCol; } }