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 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); } } }
@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); } } }
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; }
/** * 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(); }
// 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(); }
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; }
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 byte[] getByteArray(Expression e) { return StringUtils.convertHexToBytes(e.optimize(session).getValue(session).getString()); }
private char[] getCharArray(Expression e) { return e.optimize(session).getValue(session).getString().toCharArray(); }
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; }
@Override public int update() { session.commit(true); Database db = session.getDatabase(); session.getUser().checkRight(table, Right.ALL); table.checkSupportAlter(); table.lock(session, true, true); if (newColumn != null) { checkDefaultReferencesTable(newColumn.getDefaultExpression()); } if (columnsToAdd != null) { for (Column column : columnsToAdd) { checkDefaultReferencesTable(column.getDefaultExpression()); } } switch (type) { case CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL: { if (!oldColumn.isNullable()) { // no change break; } checkNoNullValues(); oldColumn.setNullable(false); db.updateMeta(session, table); break; } case CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL: { if (oldColumn.isNullable()) { // no change break; } checkNullable(); oldColumn.setNullable(true); db.updateMeta(session, table); break; } case CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT: { Sequence sequence = oldColumn == null ? null : oldColumn.getSequence(); checkDefaultReferencesTable(defaultExpression); oldColumn.setSequence(null); oldColumn.setDefaultExpression(session, defaultExpression); removeSequence(sequence); db.updateMeta(session, table); break; } case CommandInterface.ALTER_TABLE_ALTER_COLUMN_CHANGE_TYPE: { // if the change is only increasing the precision, then we don't // need to copy the table because the length is only a constraint, // and does not affect the storage structure. if (oldColumn.isWideningConversion(newColumn)) { convertAutoIncrementColumn(newColumn); oldColumn.copy(newColumn); db.updateMeta(session, table); } else { oldColumn.setSequence(null); oldColumn.setDefaultExpression(session, null); oldColumn.setConvertNullToDefault(false); if (oldColumn.isNullable() && !newColumn.isNullable()) { checkNoNullValues(); } else if (!oldColumn.isNullable() && newColumn.isNullable()) { checkNullable(); } convertAutoIncrementColumn(newColumn); copyData(); } break; } case CommandInterface.ALTER_TABLE_ADD_COLUMN: { // ifNotExists only supported for single column add if (ifNotExists && columnsToAdd.size() == 1 && table.doesColumnExist(columnsToAdd.get(0).getName())) { break; } for (Column column : columnsToAdd) { if (column.isAutoIncrement()) { int objId = getObjectId(); column.convertAutoIncrementToSequence( session, getSchema(), objId, table.isTemporary()); } } copyData(); break; } case CommandInterface.ALTER_TABLE_DROP_COLUMN: { if (table.getColumns().length - columnsToRemove.size() < 1) { throw DbException.get( ErrorCode.CANNOT_DROP_LAST_COLUMN, columnsToRemove.get(0).getSQL()); } table.dropMultipleColumnsConstraintsAndIndexes(session, columnsToRemove); copyData(); break; } case CommandInterface.ALTER_TABLE_ALTER_COLUMN_SELECTIVITY: { int value = newSelectivity.optimize(session).getValue(session).getInt(); oldColumn.setSelectivity(value); db.updateMeta(session, table); break; } default: DbException.throwInternalError("type=" + type); } return 0; }