private void copyData() { if (table.isTemporary()) { throw DbException.getUnsupportedException("TEMP TABLE"); } Database db = session.getDatabase(); String baseName = table.getName(); String tempName = db.getTempTableName(baseName, session); Column[] columns = table.getColumns(); ArrayList<Column> newColumns = New.arrayList(); Table newTable = cloneTableStructure(columns, db, tempName, newColumns); try { // check if a view would become invalid // (because the column to drop is referenced or so) checkViews(table, newTable); } catch (DbException e) { execute("DROP TABLE " + newTable.getName(), true); throw DbException.get(ErrorCode.VIEW_IS_INVALID_2, e, getSQL(), e.getMessage()); } String tableName = table.getName(); ArrayList<TableView> views = table.getViews(); if (views != null) { views = New.arrayList(views); for (TableView view : views) { table.removeView(view); } } execute("DROP TABLE " + table.getSQL() + " IGNORE", true); db.renameSchemaObject(session, newTable, tableName); for (DbObject child : newTable.getChildren()) { if (child instanceof Sequence) { continue; } String name = child.getName(); if (name == null || child.getCreateSQL() == null) { continue; } if (name.startsWith(tempName + "_")) { name = name.substring(tempName.length() + 1); SchemaObject so = (SchemaObject) child; if (so instanceof Constraint) { if (so.getSchema().findConstraint(session, name) != null) { name = so.getSchema().getUniqueConstraintName(session, newTable); } } else if (so instanceof Index) { if (so.getSchema().findIndex(session, name) != null) { name = so.getSchema().getUniqueIndexName(session, newTable, name); } } db.renameSchemaObject(session, so, name); } } if (views != null) { for (TableView view : views) { String sql = view.getCreateSQL(true, true); execute(sql, true); } } }
/** * Get all objects. * * @return a (possible empty) list of all objects */ public ArrayList<SchemaObject> getAll() { ArrayList<SchemaObject> all = New.arrayList(); all.addAll(getMap(DbObjectType.TABLE_OR_VIEW).values()); all.addAll(getMap(DbObjectType.SEQUENCE).values()); all.addAll(getMap(DbObjectType.INDEX).values()); all.addAll(getMap(DbObjectType.TRIGGER).values()); all.addAll(getMap(DbObjectType.CONSTRAINT).values()); all.addAll(getMap(DbObjectType.CONSTANT).values()); all.addAll(getMap(DbObjectType.FUNCTION_ALIAS).values()); return all; }
@Override public void removeChildrenAndResources(ServerSession session) { while (triggers != null && triggers.size() > 0) { TriggerObject obj = (TriggerObject) triggers.values().toArray()[0]; database.removeSchemaObject(session, obj); } while (constraints != null && constraints.size() > 0) { Constraint obj = (Constraint) constraints.values().toArray()[0]; database.removeSchemaObject(session, obj); } // There can be dependencies between tables e.g. using computed columns, // so we might need to loop over them multiple times. boolean runLoopAgain = false; do { runLoopAgain = false; if (tablesAndViews != null) { // Loop over a copy because the map is modified underneath us. for (Table obj : New.arrayList(tablesAndViews.values())) { // Check for null because multiple tables might be deleted // in one go underneath us. if (obj.getName() != null) { if (database.getDependentTable(obj, obj) == null) { database.removeSchemaObject(session, obj); } else { runLoopAgain = true; } } } } } while (runLoopAgain); while (indexes != null && indexes.size() > 0) { Index obj = (Index) indexes.values().toArray()[0]; database.removeSchemaObject(session, obj); } while (sequences != null && sequences.size() > 0) { Sequence obj = (Sequence) sequences.values().toArray()[0]; database.removeSchemaObject(session, obj); } while (constants != null && constants.size() > 0) { Constant obj = (Constant) constants.values().toArray()[0]; database.removeSchemaObject(session, obj); } while (functions != null && functions.size() > 0) { FunctionAlias obj = (FunctionAlias) functions.values().toArray()[0]; database.removeSchemaObject(session, obj); } owner = null; super.removeChildrenAndResources(session); }
private ArrayList<BTreeChunk> compactGetOldChunks(int targetFillRate, int write) { if (lastChunk == null) { // nothing to do return null; } readAllChunks(); // calculate the fill rate long maxLengthSum = 0; long maxLengthLiveSum = 0; long time = getTimeSinceCreation(); for (BTreeChunk c : chunks.values()) { // ignore young chunks, because we don't optimize those if (c.time + retentionTime > time) { continue; } maxLengthSum += c.maxLen; maxLengthLiveSum += c.maxLenLive; } if (maxLengthLiveSum < 0) { // no old data return null; } // the fill rate of all chunks combined if (maxLengthSum <= 0) { // avoid division by 0 maxLengthSum = 1; } int fillRate = (int) (100 * maxLengthLiveSum / maxLengthSum); if (fillRate >= targetFillRate) { return null; } // the 'old' list contains the chunks we want to free up ArrayList<BTreeChunk> old = New.arrayList(); BTreeChunk last = chunks.get(lastChunk.id); for (BTreeChunk c : chunks.values()) { // only look at chunk older than the retention time // (it's possible to compact chunks earlier, but right // now we don't do that) if (c.time + retentionTime > time) { continue; } long age = last.version - c.version + 1; c.collectPriority = (int) (c.getFillRate() * 1000 / age); old.add(c); } if (old.size() == 0) { return null; } // sort the list, so the first entry should be collected first Collections.sort( old, new Comparator<BTreeChunk>() { @Override public int compare(BTreeChunk o1, BTreeChunk o2) { int comp = new Integer(o1.collectPriority).compareTo(o2.collectPriority); if (comp == 0) { comp = new Long(o1.maxLenLive).compareTo(o2.maxLenLive); } return comp; } }); // find out up to were in the old list we need to move long written = 0; int chunkCount = 0; BTreeChunk move = null; for (BTreeChunk c : old) { if (move != null) { if (c.collectPriority > 0 && written > write) { break; } } written += c.maxLenLive; chunkCount++; move = c; } if (chunkCount < 1) { return null; } // remove the chunks we want to keep from this list boolean remove = false; for (Iterator<BTreeChunk> it = old.iterator(); it.hasNext(); ) { BTreeChunk c = it.next(); if (move == c) { remove = true; } else if (remove) { it.remove(); } } return old; }
/** * Get all tables and views. * * @return a (possible empty) list of all objects */ public ArrayList<Table> getAllTablesAndViews() { synchronized (database) { return New.arrayList(tablesAndViews.values()); } }
/** * Get all objects of the given type. * * @param type the object type * @return a (possible empty) list of all objects */ public ArrayList<SchemaObject> getAll(DbObjectType type) { HashMap<String, SchemaObject> map = getMap(type); return New.arrayList(map.values()); }
private Table cloneTableStructure( Column[] columns, Database db, String tempName, ArrayList<Column> newColumns) { for (Column col : columns) { newColumns.add(col.getClone()); } if (type == CommandInterface.ALTER_TABLE_DROP_COLUMN) { int position = oldColumn.getColumnId(); newColumns.remove(position); } else if (type == CommandInterface.ALTER_TABLE_ADD_COLUMN) { int position; if (addBefore != null) { position = table.getColumn(addBefore).getColumnId(); } else if (addAfter != null) { position = table.getColumn(addAfter).getColumnId() + 1; } else { position = columns.length; } for (Column column : columnsToAdd) { newColumns.add(position++, column); } } else if (type == CommandInterface.ALTER_TABLE_ALTER_COLUMN_CHANGE_TYPE) { int position = oldColumn.getColumnId(); newColumns.remove(position); newColumns.add(position, newColumn); } // create a table object in order to get the SQL statement // can't just use this table, because most column objects are 'shared' // with the old table // still need a new id because using 0 would mean: the new table tries // to use the rows of the table 0 (the meta table) int id = db.allocateObjectId(); CreateTableData data = new CreateTableData(); data.tableName = tempName; data.id = id; data.columns = newColumns; data.temporary = table.isTemporary(); data.persistData = table.isPersistData(); data.persistIndexes = table.isPersistIndexes(); data.isHidden = table.isHidden(); data.create = true; data.session = session; data.storageEngineName = table.getStorageEngineName(); Table newTable = getSchema().createTable(data); newTable.setComment(table.getComment()); StringBuilder buff = new StringBuilder(); buff.append(newTable.getCreateSQL()); if (table.supportsAlterColumnWithCopyData()) { StringBuilder columnList = new StringBuilder(); for (Column nc : newColumns) { if (columnList.length() > 0) { columnList.append(", "); } if (type == CommandInterface.ALTER_TABLE_ADD_COLUMN && columnsToAdd.contains(nc)) { Expression def = (Expression) nc.getDefaultExpression(); columnList.append(def == null ? "NULL" : def.getSQL()); } else { columnList.append(nc.getSQL()); } } buff.append(" AS SELECT "); if (columnList.length() == 0) { // special case: insert into test select * from buff.append('*'); } else { buff.append(columnList); } buff.append(" FROM ").append(table.getSQL()); } String newTableSQL = buff.toString(); String newTableName = newTable.getName(); Schema newTableSchema = newTable.getSchema(); newTable.removeChildrenAndResources(session); execute(newTableSQL, true); newTable = newTableSchema.getTableOrView(session, newTableName); ArrayList<String> triggers = New.arrayList(); for (DbObject child : table.getChildren()) { if (child instanceof Sequence) { continue; } else if (child instanceof Index) { Index idx = (Index) child; if (idx.getIndexType().getBelongsToConstraint()) { continue; } } String createSQL = child.getCreateSQL(); if (createSQL == null) { continue; } if (child instanceof TableView) { continue; } else if (child.getType() == DbObject.TABLE_OR_VIEW) { DbException.throwInternalError(); } String quotedName = Parser.quoteIdentifier(tempName + "_" + child.getName()); String sql = null; if (child instanceof ConstraintReferential) { ConstraintReferential r = (ConstraintReferential) child; if (r.getTable() != table) { sql = r.getCreateSQLForCopy(r.getTable(), newTable, quotedName, false); } } if (sql == null) { sql = child.getCreateSQLForCopy(newTable, quotedName); } if (sql != null) { if (child instanceof TriggerObject) { triggers.add(sql); } else { execute(sql, true); } } } table.setModified(); // remove the sequences from the columns (except dropped columns) // otherwise the sequence is dropped if the table is dropped for (Column col : newColumns) { Sequence seq = col.getSequence(); if (seq != null) { table.removeSequence(seq); col.setSequence(null); } } for (String sql : triggers) { execute(sql, true); } return newTable; }
@Override public int update() { if (!transactional) { session.commit(true); } Database db = session.getDatabase(); if (!db.isPersistent()) { data.persistIndexes = false; } if (getSchema().findTableOrView(session, data.tableName) != null) { if (ifNotExists) { return 0; } throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, data.tableName); } if (asQuery != null) { asQuery.prepare(); if (data.columns.isEmpty()) { generateColumnsFromQuery(); } else if (data.columns.size() != asQuery.getColumnCount()) { throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); } } if (pkColumns != null) { for (Column c : data.columns) { for (IndexColumn idxCol : pkColumns) { if (c.getName().equals(idxCol.columnName)) { c.setNullable(false); } } } } data.id = getObjectId(); data.create = create; data.session = session; boolean isSessionTemporary = data.temporary && !data.globalTemporary; if (!isSessionTemporary) { db.lockMeta(session); } Table table = createTable(data); ArrayList<Sequence> sequences = New.arrayList(); for (Column c : data.columns) { if (c.isAutoIncrement()) { int objId = getObjectId(); c.convertAutoIncrementToSequence(session, getSchema(), objId, data.temporary); } Sequence seq = c.getSequence(); if (seq != null) { sequences.add(seq); } } table.setComment(comment); if (isSessionTemporary) { if (onCommitDrop) { table.setOnCommitDrop(true); } if (onCommitTruncate) { table.setOnCommitTruncate(true); } session.addLocalTempTable(table); } else { db.lockMeta(session); db.addSchemaObject(session, table); } try { for (Column c : data.columns) { c.prepareExpression(session); } for (Sequence sequence : sequences) { table.addSequence(sequence); } for (DefineCommand command : constraintCommands) { command.setTransactional(transactional); command.update(); } if (asQuery != null) { Insert insert = null; insert = new Insert(session); insert.setSortedInsertMode(sortedInsertMode); insert.setQuery(asQuery); insert.setTable(table); insert.setInsertFromSelect(true); insert.prepare(); insert.update(); } } catch (DbException e) { db.checkPowerOff(); db.removeSchemaObject(session, table); if (!transactional) { session.commit(true); } throw e; } return 0; }
/** This class represents the statement CREATE TABLE */ public class CreateTable extends SchemaCommand { protected final CreateTableData data = new CreateTableData(); protected IndexColumn[] pkColumns; protected boolean ifNotExists; private final ArrayList<DefineCommand> constraintCommands = New.arrayList(); private boolean onCommitDrop; private boolean onCommitTruncate; private Query asQuery; private String comment; private boolean sortedInsertMode; public CreateTable(Session session, Schema schema) { super(session, schema); data.persistIndexes = true; data.persistData = true; } public void setQuery(Query query) { this.asQuery = query; } public void setTemporary(boolean temporary) { data.temporary = temporary; } public void setTableName(String tableName) { data.tableName = tableName; } /** * Add a column to this table. * * @param column the column to add */ public void addColumn(Column column) { data.columns.add(column); } /** * Add a constraint statement to this statement. The primary key definition is one possible * constraint statement. * * @param command the statement to add */ public void addConstraintCommand(DefineCommand command) { if (command instanceof CreateIndex) { constraintCommands.add(command); } else { AlterTableAddConstraint con = (AlterTableAddConstraint) command; boolean alreadySet; if (con.getType() == CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_PRIMARY_KEY) { alreadySet = setPrimaryKeyColumns(con.getIndexColumns()); } else { alreadySet = false; } if (!alreadySet) { constraintCommands.add(command); } } } public void setIfNotExists(boolean ifNotExists) { this.ifNotExists = ifNotExists; } @Override public int update() { if (!transactional) { session.commit(true); } Database db = session.getDatabase(); if (!db.isPersistent()) { data.persistIndexes = false; } if (getSchema().findTableOrView(session, data.tableName) != null) { if (ifNotExists) { return 0; } throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, data.tableName); } if (asQuery != null) { asQuery.prepare(); if (data.columns.isEmpty()) { generateColumnsFromQuery(); } else if (data.columns.size() != asQuery.getColumnCount()) { throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); } } if (pkColumns != null) { for (Column c : data.columns) { for (IndexColumn idxCol : pkColumns) { if (c.getName().equals(idxCol.columnName)) { c.setNullable(false); } } } } data.id = getObjectId(); data.create = create; data.session = session; boolean isSessionTemporary = data.temporary && !data.globalTemporary; if (!isSessionTemporary) { db.lockMeta(session); } Table table = createTable(data); ArrayList<Sequence> sequences = New.arrayList(); for (Column c : data.columns) { if (c.isAutoIncrement()) { int objId = getObjectId(); c.convertAutoIncrementToSequence(session, getSchema(), objId, data.temporary); } Sequence seq = c.getSequence(); if (seq != null) { sequences.add(seq); } } table.setComment(comment); if (isSessionTemporary) { if (onCommitDrop) { table.setOnCommitDrop(true); } if (onCommitTruncate) { table.setOnCommitTruncate(true); } session.addLocalTempTable(table); } else { db.lockMeta(session); db.addSchemaObject(session, table); } try { for (Column c : data.columns) { c.prepareExpression(session); } for (Sequence sequence : sequences) { table.addSequence(sequence); } for (DefineCommand command : constraintCommands) { command.setTransactional(transactional); command.update(); } if (asQuery != null) { Insert insert = null; insert = new Insert(session); insert.setSortedInsertMode(sortedInsertMode); insert.setQuery(asQuery); insert.setTable(table); insert.setInsertFromSelect(true); insert.prepare(); insert.update(); } } catch (DbException e) { db.checkPowerOff(); db.removeSchemaObject(session, table); if (!transactional) { session.commit(true); } throw e; } return 0; } protected Table createTable(CreateTableData data) { return getSchema().createTable(data); } 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); } } /** * Sets the primary key columns, but also check if a primary key with different columns is already * defined. * * @param columns the primary key columns * @return true if the same primary key columns where already set */ private boolean setPrimaryKeyColumns(IndexColumn[] columns) { if (pkColumns != null) { int len = columns.length; if (len != pkColumns.length) { throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY); } for (int i = 0; i < len; i++) { if (!columns[i].columnName.equals(pkColumns[i].columnName)) { throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY); } } return true; } this.pkColumns = columns; return false; } public void setPersistIndexes(boolean persistIndexes) { data.persistIndexes = persistIndexes; } public void setGlobalTemporary(boolean globalTemporary) { data.globalTemporary = globalTemporary; } /** This temporary table is dropped on commit. */ public void setOnCommitDrop() { this.onCommitDrop = true; } /** This temporary table is truncated on commit. */ public void setOnCommitTruncate() { this.onCommitTruncate = true; } public void setComment(String comment) { this.comment = comment; } public void setPersistData(boolean persistData) { data.persistData = persistData; if (!persistData) { data.persistIndexes = false; } } public void setSortedInsertMode(boolean sortedInsertMode) { this.sortedInsertMode = sortedInsertMode; } public void setStorageEngine(String storageEngine) { data.storageEngine = storageEngine; } public void setHidden(boolean isHidden) { data.isHidden = isHidden; } @Override public int getType() { return CommandInterface.CREATE_TABLE; } }