private Value[] convert(Value[] values, int columnCount) { for (int i = 0; i < columnCount; i++) { Expression e = expressions.get(i); values[i] = values[i].convertTo(e.getType()); } return values; }
@Override public void prepare() { if (columns == null) { // 如INSERT INTO InsertTest DEFAULT VALUES if (list.size() > 0 && list.get(0).length == 0) { // special case where table is used as a sequence columns = new Column[0]; } else { // 如INSERT INTO InsertTest(SELECT * FROM tmpSelectTest) columns = table.getColumns(); } } if (list.size() > 0) { for (Expression[] expr : list) { if (expr.length != columns.length) { throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); } for (int i = 0, len = expr.length; i < len; i++) { Expression e = expr[i]; if (e != null) { e = e.optimize(session); if (e instanceof Parameter) { Parameter p = (Parameter) e; p.setColumn(columns[i]); } expr[i] = e; } } } } else { query.prepare(); if (query.getColumnCount() != columns.length) { throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); } } }
public boolean isEverything(ExpressionVisitor visitor) { switch (visitor.getType()) { case ExpressionVisitor.DETERMINISTIC: { if (isForUpdate) { return false; } for (int i = 0, size = filters.size(); i < size; i++) { TableFilter f = filters.get(i); if (!f.getTable().isDeterministic()) { return false; } } break; } case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID: { for (int i = 0, size = filters.size(); i < size; i++) { TableFilter f = filters.get(i); long m = f.getTable().getMaxDataModificationId(); visitor.addDataModificationId(m); } break; } case ExpressionVisitor.EVALUATABLE: { if (!session.getDatabase().getSettings().optimizeEvaluatableSubqueries) { return false; } break; } case ExpressionVisitor.GET_DEPENDENCIES: { for (int i = 0, size = filters.size(); i < size; i++) { TableFilter f = filters.get(i); Table table = f.getTable(); visitor.addDependency(table); table.addDependencies(visitor.getDependencies()); } break; } default: } ExpressionVisitor v2 = visitor.incrementQueryLevel(1); boolean result = true; for (int i = 0, size = expressions.size(); i < size; i++) { Expression e = expressions.get(i); if (!e.isEverything(v2)) { result = false; break; } } if (result && condition != null && !condition.isEverything(v2)) { result = false; } if (result && having != null && !having.isEverything(v2)) { result = false; } return result; }
private int insertRows() { session.getUser().checkRight(table, Right.INSERT); setCurrentRowNumber(0); table.fire(session, Trigger.INSERT, true); rowNumber = 0; int listSize = list.size(); if (listSize > 0) { int columnLen = columns.length; for (int x = 0; x < listSize; x++) { Row newRow = table.getTemplateRow(); // newRow的长度是全表字段的个数是,会>=columns的长度 Expression[] expr = list.get(x); setCurrentRowNumber(x + 1); for (int i = 0; i < columnLen; i++) { Column c = columns[i]; int index = c.getColumnId(); // 从0开始 Expression e = expr[i]; if (e != null) { // e can be null (DEFAULT) e = e.optimize(session); try { Value v = c.convert(e.getValue(session)); newRow.setValue(index, v); } catch (DbException ex) { throw setRow(ex, x, getSQL(expr)); } } } rowNumber++; table.validateConvertUpdateSequence(session, newRow); boolean done = table.fireBeforeRow(session, null, newRow); // INSTEAD OF触发器会返回true if (!done) { // 直到事务commit或rollback时才解琐,见org.h2.engine.Session.unlockAll() table.lock(session, true, false); table.addRow(session, newRow); // 在org.h2.index.PageDataIndex.addTry(Session, Row)中事先记了一次PageLog // 也就是org.h2.store.PageStore.logAddOrRemoveRow(Session, int, Row, boolean) // 这里又记了一次UndoLog // UndoLog在org.h2.engine.Session.commit(boolean)时就清除了 session.log(table, UndoLogRecord.INSERT, newRow); table.fireAfterRow(session, null, newRow, false); } } } else { table.lock(session, true, false); // 这种方式主要是避免循环两次,因为query内部己循环一次了,得到记录后像else中的非insertFromSelect一样,还要循环一次 if (insertFromSelect) { query.query(0, this); // 每遍历一行会回调下面的addRow方法 } else { ResultInterface rows = query.query(0); while (rows.next()) { Value[] r = rows.currentRow(); addRow(r); } rows.close(); } } table.fire(session, Trigger.INSERT, false); return rowNumber; }
private void queryQuick(int columnCount, ResultTarget result) { Value[] row = new Value[columnCount]; for (int i = 0; i < columnCount; i++) { Expression expr = expressions.get(i); row[i] = expr.getValue(session); } result.addRow(row); }
public void setEvaluatable(TableFilter tableFilter, boolean b) { for (Expression e : expressions) { e.setEvaluatable(tableFilter, b); } if (condition != null) { condition.setEvaluatable(tableFilter, b); } }
@Override public String getSQL() { String sql = NAME + "(" + min.getSQL() + ", " + max.getSQL(); if (step != null) { sql += ", " + step.getSQL(); } return sql + ")"; }
public void mapColumns(ColumnResolver resolver, int level) { for (Expression e : expressions) { e.mapColumns(resolver, level); } if (condition != null) { condition.mapColumns(resolver, level); } }
private void optimize(Session s) { if (!optimized) { min = min.optimize(s); max = max.optimize(s); if (step != null) { step = step.optimize(s); } optimized = true; } }
private void checkDefaultReferencesTable(Expression defaultExpression) { if (defaultExpression == null) { return; } HashSet<DbObject> dependencies = New.hashSet(); ExpressionVisitor visitor = ExpressionVisitor.getDependenciesVisitor(dependencies); defaultExpression.isEverything(visitor); if (dependencies.contains(table)) { throw DbException.get(ErrorCode.COLUMN_IS_REFERENCED_1, defaultExpression.getSQL()); } }
public void updateAggregate(Session s) { for (Expression e : expressions) { e.updateAggregate(s); } if (condition != null) { condition.updateAggregate(s); } if (having != null) { having.updateAggregate(s); } }
// limitExpr在org.h2.command.Parser.parseDelete()中调用过optimize了,所以在这里不用再调用 // 因为limitExpr不会涉及到列,所以也不需要调用mapColumns @Override public void prepare() { if (condition != null) { condition.mapColumns(tableFilter, 0); condition = condition.optimize(session); condition.createIndexConditions(session, tableFilter); } // 为什么不能像mapColumns把level设为0,因为getBestPlanItem内部会把level当被除数,所以不行。 PlanItem item = tableFilter.getBestPlanItem(session, 1); tableFilter.setPlanItem(item); tableFilter.prepare(); }
@Override public String getPlanSQL() { StringBuilder buff = new StringBuilder(); buff.append("DELETE "); buff.append("FROM ").append(tableFilter.getPlanSQL(false)); if (condition != null) { buff.append("\nWHERE ").append(StringUtils.unEnclose(condition.getSQL())); } if (limitExpr != null) { buff.append("\nLIMIT (").append(StringUtils.unEnclose(limitExpr.getSQL())).append(')'); } return buff.toString(); }
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(); } }
private void expandColumnList() { Database db = session.getDatabase(); // the expressions may change within the loop for (int i = 0; i < expressions.size(); i++) { Expression expr = expressions.get(i); if (!expr.isWildcard()) { continue; } String schemaName = expr.getSchemaName(); String tableAlias = expr.getTableAlias(); if (tableAlias == null) { int temp = i; expressions.remove(i); for (TableFilter filter : filters) { Wildcard c2 = new Wildcard(filter.getTable().getSchema().getName(), filter.getTableAlias()); expressions.add(i++, c2); } i = temp - 1; } else { TableFilter filter = null; for (TableFilter f : filters) { if (db.equalsIdentifiers(tableAlias, f.getTableAlias())) { if (schemaName == null || db.equalsIdentifiers(schemaName, f.getSchemaName())) { filter = f; break; } } } if (filter == null) { throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableAlias); } Table t = filter.getTable(); String alias = filter.getTableAlias(); expressions.remove(i); Column[] columns = t.getColumns(); for (Column c : columns) { if (filter.isNaturalJoinColumn(c)) { continue; } ExpressionColumn ec = new ExpressionColumn(session.getDatabase(), null, alias, c.getName()); expressions.add(i++, ec); } i--; } } }
private void setEvaluatableRecursive(TableFilter f) { for (; f != null; f = f.getJoin()) { f.setEvaluatable(f, true); if (condition != null) { condition.setEvaluatable(f, true); } TableFilter n = f.getNestedJoin(); if (n != null) { setEvaluatableRecursive(n); } Expression on = f.getJoinCondition(); if (on != null) { if (!on.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) { if (session.getDatabase().getSettings().nestedJoins) { // need to check that all added are bound to a table on = on.optimize(session); if (!f.isJoinOuter() && !f.isJoinOuterIndirect()) { f.removeJoinCondition(); addCondition(on); } } else { if (f.isJoinOuter()) { // this will check if all columns exist - it may or may not throw an exception on = on.optimize(session); // it is not supported even if the columns exist throw DbException.get(ErrorCode.UNSUPPORTED_OUTER_JOIN_CONDITION_1, on.getSQL()); } f.removeJoinCondition(); // need to check that all added are bound to a table on = on.optimize(session); addCondition(on); } } } on = f.getFilterCondition(); if (on != null) { if (!on.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) { f.removeFilterCondition(); addCondition(on); } } // this is only important for subqueries, so they know // the result columns are evaluatable for (Expression e : expressions) { e.setEvaluatable(f, true); } } }
/** * Get the increment. * * @param session the session * @return the increment (1 by default) */ public long getStep(Session session) { optimize(session); if (step == null) { return 1; } return step.getValue(session).getLong(); }
private void addGroupSortedRow(Value[] keyValues, int columnCount, ResultTarget result) { Value[] row = new Value[columnCount]; for (int j = 0; groupIndex != null && j < groupIndex.length; j++) { row[groupIndex[j]] = keyValues[j]; } for (int j = 0; j < columnCount; j++) { if (groupByExpression != null && groupByExpression[j]) { continue; } Expression expr = expressions.get(j); row[j] = expr.getValue(session); } if (isHavingNullOrFalse(row)) { return; } row = keepOnlyDistinct(row, columnCount); result.addRow(row); }
public int update() throws SQLException { session.commit(true); session.getUser().checkAdmin(); Database db = session.getDatabase(); if (getSchema().findConstant(constantName) != null) { if (ifNotExists) { return 0; } throw Message.getSQLException(ErrorCode.CONSTANT_ALREADY_EXISTS_1, constantName); } int id = getObjectId(false, true); Constant constant = new Constant(getSchema(), id, constantName); expression = expression.optimize(session); Value value = expression.getValue(session); constant.setValue(value); db.addSchemaObject(session, constant); return 0; }
/** * Get the sample size, if set. * * @param session the session * @return the sample size */ int getSampleSizeValue(Session session) { if (sampleSizeExpr == null) { return 0; } Value v = sampleSizeExpr.optimize(session).getValue(session); if (v == ValueNull.INSTANCE) { return 0; } return v.getInt(); }
private void queryFlat(int columnCount, ResultTarget result, long limitRows) { // limitRows must be long, otherwise we get an int overflow // if limitRows is at or near Integer.MAX_VALUE // limitRows is never 0 here if (limitRows > 0 && offsetExpr != null) { int offset = offsetExpr.getValue(session).getInt(); if (offset > 0) { limitRows += offset; } } int rowNumber = 0; setCurrentRowNumber(0); ArrayList<Row> forUpdateRows = null; if (isForUpdateMvcc) { forUpdateRows = New.arrayList(); } while (topTableFilter.next()) { setCurrentRowNumber(rowNumber + 1); if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) { Value[] row = new Value[columnCount]; for (int i = 0; i < columnCount; i++) { Expression expr = expressions.get(i); row[i] = expr.getValue(session); } if (isForUpdateMvcc) { topTableFilter.lockRowAdd(forUpdateRows); } result.addRow(row); rowNumber++; if ((sort == null || sortUsingIndex) && limitRows > 0 && result.getRowCount() >= limitRows) { break; } if (sampleSize > 0 && rowNumber >= sampleSize) { break; } } } if (isForUpdateMvcc) { topTableFilter.lockRows(forUpdateRows); } }
@Override public String getPlanSQL() { StatementBuilder buff = new StatementBuilder("INSERT INTO "); buff.append(table.getSQL()).append('('); for (Column c : columns) { buff.appendExceptFirst(", "); buff.append(c.getSQL()); } buff.append(")\n"); if (insertFromSelect) { buff.append("DIRECT "); } if (sortedInsertMode) { buff.append("SORTED "); } if (list.size() > 0) { buff.append("VALUES "); int row = 0; if (list.size() > 1) { buff.append('\n'); } for (Expression[] expr : list) { if (row++ > 0) { buff.append(",\n"); } buff.append('('); buff.resetCount(); for (Expression e : expr) { buff.appendExceptFirst(", "); if (e == null) { buff.append("DEFAULT"); } else { buff.append(e.getSQL()); } } buff.append(')'); } } else { buff.append(query.getPlanSQL()); } return buff.toString(); }
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); } } }
private void generateColumnsFromQuery() { int columnCount = asQuery.getColumnCount(); ArrayList<Expression> expressions = asQuery.getExpressions(); for (int i = 0; i < columnCount; i++) { Expression expr = expressions.get(i); int type = expr.getType(); String name = expr.getAlias(); long precision = expr.getPrecision(); int displaySize = expr.getDisplaySize(); DataType dt = DataType.getDataType(type); if (precision > 0 && (dt.defaultPrecision == 0 || (dt.defaultPrecision > precision && dt.defaultPrecision < Byte.MAX_VALUE))) { // dont' set precision to MAX_VALUE if this is the default precision = dt.defaultPrecision; } int scale = expr.getScale(); if (scale > 0 && (dt.defaultScale == 0 || (dt.defaultScale > scale && dt.defaultScale < precision))) { scale = dt.defaultScale; } if (scale > precision) { precision = scale; } Column col = new Column(name, type, precision, scale, displaySize); addColumn(col); } }
/** * Create a {@link SortOrder} object given the list of {@link SelectOrderBy} objects. The * expression list is extended if necessary. * * @param orderList a list of {@link SelectOrderBy} elements * @param expressionCount the number of columns in the query * @return the {@link SortOrder} object */ public SortOrder prepareOrder(ArrayList<SelectOrderBy> orderList, int expressionCount) { int size = orderList.size(); int[] index = new int[size]; int[] sortType = new int[size]; for (int i = 0; i < size; i++) { SelectOrderBy o = orderList.get(i); int idx; boolean reverse = false; Expression expr = o.columnIndexExpr; Value v = expr.getValue(null); if (v == ValueNull.INSTANCE) { // parameter not yet set - order by first column idx = 0; } else { idx = v.getInt(); if (idx < 0) { reverse = true; idx = -idx; } idx -= 1; if (idx < 0 || idx >= expressionCount) { throw DbException.get(ErrorCode.ORDER_BY_NOT_IN_RESULT, "" + (idx + 1)); } } index[i] = idx; boolean desc = o.descending; if (reverse) { desc = !desc; } int type = desc ? SortOrder.DESCENDING : SortOrder.ASCENDING; if (o.nullsFirst) { type += SortOrder.NULLS_FIRST; } else if (o.nullsLast) { type += SortOrder.NULLS_LAST; } sortType[i] = type; } return new SortOrder(session.getDatabase(), index, sortType, orderList); }
private void queryGroupSorted(int columnCount, ResultTarget result) { int rowNumber = 0; setCurrentRowNumber(0); Value[] previousKeyValues = null; while (topTableFilter.next()) { setCurrentRowNumber(rowNumber + 1); if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) { rowNumber++; Value[] keyValues = new Value[groupIndex.length]; // update group for (int i = 0; i < groupIndex.length; i++) { int idx = groupIndex[i]; Expression expr = expressions.get(idx); keyValues[i] = expr.getValue(session); } if (previousKeyValues == null) { previousKeyValues = keyValues; currentGroup = New.hashMap(); } else if (!Arrays.equals(previousKeyValues, keyValues)) { addGroupSortedRow(previousKeyValues, columnCount, result); previousKeyValues = keyValues; currentGroup = New.hashMap(); } currentGroupRowId++; for (int i = 0; i < columnCount; i++) { if (groupByExpression == null || !groupByExpression[i]) { Expression expr = expressions.get(i); expr.updateAggregate(session); } } } } if (previousKeyValues != null) { addGroupSortedRow(previousKeyValues, columnCount, result); } }
private void initColumnsAndTables(Session session) { Column[] cols; removeViewFromTables(); try { Query query = recompileQuery(session); tables = New.arrayList(query.getTables()); ArrayList<Expression> expressions = query.getExpressions(); ArrayList<Column> list = New.arrayList(); for (int i = 0; i < query.getColumnCount(); i++) { Expression expr = expressions.get(i); String name = null; if (columnNames != null && columnNames.length > i) { name = columnNames[i]; } if (name == null) { name = expr.getAlias(); } int type = expr.getType(); long precision = expr.getPrecision(); int scale = expr.getScale(); int displaySize = expr.getDisplaySize(); Column col = new Column(name, type, precision, scale, displaySize); col.setTable(this, i); list.add(col); } cols = new Column[list.size()]; list.toArray(cols); createException = null; viewQuery = query; } catch (DbException e) { createException = e; // if it can't be compiled, then it's a 'zero column table' // this avoids problems when creating the view when opening the // database tables = New.arrayList(); cols = new Column[0]; if (recursive && columnNames != null) { cols = new Column[columnNames.length]; for (int i = 0; i < columnNames.length; i++) { cols[i] = new Column(columnNames[i], Value.STRING); } index.setRecursive(true); createException = null; } } setColumns(cols); if (getId() != 0) { addViewToTables(); } }
@Override public int update() { tableFilter.startQuery(session); tableFilter.reset(); Table table = tableFilter.getTable(); session.getUser().checkRight(table, Right.DELETE); table.fire(session, Trigger.DELETE, true); // 直到事务commit或rollback时才解琐,见org.h2.engine.Session.unlockAll() table.lock(session, true, false); RowList rows = new RowList(session); int limitRows = -1; if (limitExpr != null) { Value v = limitExpr.getValue(session); if (v != ValueNull.INSTANCE) { limitRows = v.getInt(); } } try { setCurrentRowNumber(0); int count = 0; // 比如delete from DeleteTest limit 0, // 此时limitRows为0,不删除任何行 while (limitRows != 0 && tableFilter.next()) { setCurrentRowNumber(rows.size() + 1); // condition.getBooleanValue(session)内部会取当前行与之比较, // 比如,如果是ExpressionColumn,那么就由它对应的列,取得列id, // 然后在从当前行中按列id取当前行value数组中对应元素 if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) { Row row = tableFilter.get(); boolean done = false; if (table.fireRow()) { done = table.fireBeforeRow(session, row, null); } if (!done) { rows.add(row); } count++; if (limitRows >= 0 && count >= limitRows) { break; } } } int rowScanCount = 0; for (rows.reset(); rows.hasNext(); ) { if ((++rowScanCount & 127) == 0) { checkCanceled(); } Row row = rows.next(); table.removeRow(session, row); session.log(table, UndoLogRecord.DELETE, row); } if (table.fireRow()) { for (rows.reset(); rows.hasNext(); ) { Row row = rows.next(); table.fireAfterRow(session, row, null, false); } } table.fire(session, Trigger.DELETE, false); return count; } finally { rows.close(); } }
/** * Calculate and get the end value of this range. * * @param session the session * @return the end value */ public long getMax(Session session) { optimize(session); return max.getValue(session).getLong(); }
/** * Calculate and get the start value of this range. * * @param session the session * @return the start value */ public long getMin(Session session) { optimize(session); return min.getValue(session).getLong(); }