private void merge(Row row) { ArrayList<Parameter> k = update.getParameters(); for (int i = 0; i < columns.length; i++) { Column col = columns[i]; Value v = row.getValue(col.getColumnId()); Parameter p = k.get(i); p.setValue(v); } for (int i = 0; i < keys.length; i++) { Column col = keys[i]; Value v = row.getValue(col.getColumnId()); if (v == null) { throw DbException.get(ErrorCode.COLUMN_CONTAINS_NULL_VALUES_1, col.getSQL()); } Parameter p = k.get(columns.length + i); p.setValue(v); } int count = update.update(); if (count == 0) { try { table.validateConvertUpdateSequence(session, row); boolean done = table.fireBeforeRow(session, null, row); if (!done) { table.lock(session, true, false); table.addRow(session, row); session.log(table, UndoLogRecord.INSERT, row); table.fireAfterRow(session, null, row, false); } } catch (DbException e) { if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) { // possibly a concurrent merge or insert Index index = (Index) e.getSource(); if (index != null) { // verify the index columns match the key Column[] indexColumns = index.getColumns(); boolean indexMatchesKeys = false; if (indexColumns.length <= keys.length) { for (int i = 0; i < indexColumns.length; i++) { if (indexColumns[i] != keys[i]) { indexMatchesKeys = false; break; } } } if (indexMatchesKeys) { throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName()); } } } throw e; } } else if (count != 1) { throw DbException.get(ErrorCode.DUPLICATE_KEY_1, table.getSQL()); } }
/** * Remove the given index from the list. * * @param index the index to remove */ public void removeIndex(Index index) { ArrayList<Index> indexes = getIndexes(); if (indexes != null) { remove(indexes, index); if (index.getIndexType().isPrimaryKey()) { for (Column col : index.getColumns()) { col.setPrimaryKey(false); } } } }
public void prepare() { if (columns == null) { if (list.size() > 0 && list.get(0).length == 0) { // special case where table is used as a sequence columns = new Column[0]; } else { 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; i < expr.length; i++) { Expression e = expr[i]; if (e != null) { expr[i] = e.optimize(session); } } } } else { query.prepare(); if (query.getColumnCount() != columns.length) { throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); } } if (keys == null) { Index idx = table.getPrimaryKey(); if (idx == null) { throw DbException.get(ErrorCode.CONSTRAINT_NOT_FOUND_1, "PRIMARY KEY"); } keys = idx.getColumns(); } StatementBuilder buff = new StatementBuilder("UPDATE "); buff.append(table.getSQL()).append(" SET "); for (Column c : columns) { buff.appendExceptFirst(", "); buff.append(c.getSQL()).append("=?"); } buff.append(" WHERE "); buff.resetCount(); for (Column c : keys) { buff.appendExceptFirst(" AND "); buff.append(c.getSQL()).append("=?"); } String sql = buff.toString(); update = session.prepare(sql); }
/** * Check that this column is not referenced by a multi-column constraint or multi-column index. If * it is, an exception is thrown. Single-column references and indexes are dropped. * * @param session the session * @param col the column * @throws DbException if the column is referenced by multi-column constraints or indexes */ public void dropSingleColumnConstraintsAndIndexes(Session session, Column col) { ArrayList<Constraint> constraintsToDrop = New.arrayList(); if (constraints != null) { for (int i = 0, size = constraints.size(); i < size; i++) { Constraint constraint = constraints.get(i); HashSet<Column> columns = constraint.getReferencedColumns(this); if (!columns.contains(col)) { continue; } if (columns.size() == 1) { constraintsToDrop.add(constraint); } else { throw DbException.get(ErrorCode.COLUMN_IS_REFERENCED_1, constraint.getSQL()); } } } ArrayList<Index> indexesToDrop = New.arrayList(); ArrayList<Index> indexes = getIndexes(); if (indexes != null) { for (int i = 0, size = indexes.size(); i < size; i++) { Index index = indexes.get(i); if (index.getCreateSQL() == null) { continue; } if (index.getColumnIndex(col) < 0) { continue; } if (index.getColumns().length == 1) { indexesToDrop.add(index); } else { throw DbException.get(ErrorCode.COLUMN_IS_REFERENCED_1, index.getSQL()); } } } for (Constraint c : constraintsToDrop) { session.getDatabase().removeSchemaObject(session, c); } for (Index i : indexesToDrop) { // the index may already have been dropped when dropping the constraint if (getIndexes().contains(i)) { session.getDatabase().removeSchemaObject(session, i); } } }
private static boolean canUseIndex(Index existingIndex, Table table, IndexColumn[] cols) { if (existingIndex.getTable() != table || existingIndex.getCreateSQL() == null) { // can't use the scan index or index of another table return false; } Column[] indexCols = existingIndex.getColumns(); if (indexCols.length < cols.length) { return false; } for (IndexColumn col : cols) { // all columns of the list must be part of the index, // but not all columns of the index need to be part of the list // holes are not allowed (index=a,b,c & list=a,b is ok; but list=a,c // is not) int idx = existingIndex.getColumnIndex(col.column); if (idx < 0 || idx >= cols.length) { return false; } } return true; }
private static boolean canUseUniqueIndex(Index idx, Table table, IndexColumn[] cols) { if (idx.getTable() != table || !idx.getIndexType().isUnique()) { return false; } Column[] indexCols = idx.getColumns(); if (indexCols.length > cols.length) { return false; } HashSet<Column> set = New.hashSet(); for (IndexColumn c : cols) { set.add(c.column); } for (Column c : indexCols) { // all columns of the index must be part of the list, // but not all columns of the list need to be part of the index if (!set.contains(c)) { return false; } } return true; }