提交 9b1bca4c authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Parse constraints in ALTER TABLE ADD [ COLUMN ]

上级 65092a1d
......@@ -284,7 +284,8 @@ ALTER SEQUENCE SEQ_ID RESTART WITH 1000
"Commands (DDL)","ALTER TABLE ADD","
ALTER TABLE [ IF EXISTS ] tableName ADD [ COLUMN ]
{ [ IF NOT EXISTS ] columnName columnDefinition | ( { columnName columnDefinition } [,...] ) }
{ [ IF NOT EXISTS ] columnName columnDefinition
| ( { columnName columnDefinition | constraint } [,...] ) }
[ { { BEFORE | AFTER } columnName } | FIRST ]
","
Adds a new column to a table.
......
......@@ -48,7 +48,7 @@ import org.h2.util.New;
* ALTER TABLE ALTER COLUMN SET INVISIBLE,
* ALTER TABLE DROP COLUMN
*/
public class AlterTableAlterColumn extends SchemaCommand {
public class AlterTableAlterColumn extends CommandWithColumns {
private String tableName;
private Column oldColumn;
......@@ -175,18 +175,15 @@ public class AlterTableAlterColumn extends SchemaCommand {
}
case CommandInterface.ALTER_TABLE_ADD_COLUMN: {
// ifNotExists only supported for single column add
if (ifNotExists && columnsToAdd.size() == 1 &&
if (ifNotExists && columnsToAdd != null && 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());
}
ArrayList<Sequence> sequences = generateSequences(columnsToAdd, false);
if (columnsToAdd != null) {
changePrimaryKeysToNotNull(columnsToAdd);
}
copyData(table);
copyData(table, sequences, true);
break;
}
case CommandInterface.ALTER_TABLE_DROP_COLUMN: {
......@@ -261,6 +258,10 @@ public class AlterTableAlterColumn extends SchemaCommand {
}
private void copyData(Table table) {
copyData(table, null, false);
}
private void copyData(Table table, ArrayList<Sequence> sequences, boolean createConstraints) {
if (table.isTemporary()) {
throw DbException.getUnsupportedException("TEMP TABLE");
}
......@@ -270,6 +271,11 @@ public class AlterTableAlterColumn extends SchemaCommand {
Column[] columns = table.getColumns();
ArrayList<Column> newColumns = New.arrayList();
Table newTable = cloneTableStructure(table, columns, db, tempName, newColumns);
if (sequences != null) {
for (Sequence sequence : sequences) {
table.addSequence(sequence);
}
}
try {
// check if a view would become invalid
// (because the column to drop is referenced or so)
......@@ -308,6 +314,9 @@ public class AlterTableAlterColumn extends SchemaCommand {
db.renameSchemaObject(session, so, name);
}
}
if (createConstraints) {
createConstraints();
}
for (TableView view : dependentViews) {
String sql = view.getCreateSQL(true, true);
execute(sql, true);
......@@ -345,9 +354,11 @@ public class AlterTableAlterColumn extends SchemaCommand {
} else {
position = columns.length;
}
if (columnsToAdd != null) {
for (Column column : columnsToAdd) {
newColumns.add(position++, column);
}
}
} else if (type == CommandInterface.ALTER_TABLE_ALTER_COLUMN_CHANGE_TYPE) {
int position = oldColumn.getColumnId();
newColumns.set(position, newColumn);
......@@ -379,7 +390,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
columnList.append(", ");
}
if (type == CommandInterface.ALTER_TABLE_ADD_COLUMN &&
columnsToAdd.contains(nc)) {
columnsToAdd != null && columnsToAdd.contains(nc)) {
Expression def = nc.getDefaultExpression();
columnList.append(def == null ? "NULL" : def.getSQL());
} else {
......@@ -557,8 +568,13 @@ public class AlterTableAlterColumn extends SchemaCommand {
this.ifNotExists = ifNotExists;
}
public void setNewColumns(ArrayList<Column> columnsToAdd) {
this.columnsToAdd = columnsToAdd;
@Override
public void addColumn(Column column) {
ArrayList<Column> columnsToAdd = this.columnsToAdd;
if (columnsToAdd == null) {
this.columnsToAdd = columnsToAdd = New.arrayList();
}
columnsToAdd.add(column);
}
public void setColumnsToRemove(ArrayList<Column> columnsToRemove) {
......
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.command.ddl;
import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.util.New;
public abstract class CommandWithColumns extends SchemaCommand {
private ArrayList<DefineCommand> constraintCommands;
private IndexColumn[] pkColumns;
protected CommandWithColumns(Session session, Schema schema) {
super(session, schema);
}
/**
* Add a column to this table.
*
* @param column
* the column to add
*/
public abstract void addColumn(Column 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) {
getConstraintCommands().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) {
getConstraintCommands().add(command);
}
}
}
protected void changePrimaryKeysToNotNull(ArrayList<Column> columns) {
if (pkColumns != null) {
for (Column c : columns) {
for (IndexColumn idxCol : pkColumns) {
if (c.getName().equals(idxCol.columnName)) {
c.setNullable(false);
}
}
}
}
}
protected void createConstraints() {
ArrayList<DefineCommand> constraintCommands = this.constraintCommands;
if (constraintCommands != null) {
for (DefineCommand command : constraintCommands) {
command.setTransactional(transactional);
command.update();
}
}
}
protected ArrayList<Sequence> generateSequences(ArrayList<Column> columns, boolean temporary) {
ArrayList<Sequence> sequences = New.arrayList();
if (columns != null) {
for (Column c : columns) {
if (c.isAutoIncrement()) {
int objId = getObjectId();
c.convertAutoIncrementToSequence(session, getSchema(), objId, temporary);
if (!Constants.CLUSTERING_DISABLED.equals(session.getDatabase().getCluster())) {
throw DbException.getUnsupportedException("CLUSTERING && auto-increment columns");
}
}
Sequence seq = c.getSequence();
if (seq != null) {
sequences.add(seq);
}
}
}
return sequences;
}
private ArrayList<DefineCommand> getConstraintCommands() {
ArrayList<DefineCommand> constraintCommands = this.constraintCommands;
if (constraintCommands == null) {
this.constraintCommands = constraintCommands = New.arrayList();
}
return constraintCommands;
}
/**
* 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;
}
}
......@@ -11,7 +11,6 @@ import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.command.dml.Insert;
import org.h2.command.dml.Query;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Session;
......@@ -21,10 +20,8 @@ import org.h2.message.DbException;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.util.ColumnNamer;
import org.h2.util.New;
import org.h2.value.DataType;
import org.h2.value.Value;
......@@ -32,11 +29,9 @@ import org.h2.value.Value;
* This class represents the statement
* CREATE TABLE
*/
public class CreateTable extends SchemaCommand {
public class CreateTable extends CommandWithColumns {
private final CreateTableData data = new CreateTableData();
private final ArrayList<DefineCommand> constraintCommands = New.arrayList();
private IndexColumn[] pkColumns;
private boolean ifNotExists;
private boolean onCommitDrop;
private boolean onCommitTruncate;
......@@ -62,38 +57,11 @@ public class CreateTable extends SchemaCommand {
data.tableName = tableName;
}
/**
* Add a column to this table.
*
* @param column the column to add
*/
@Override
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;
}
......@@ -125,35 +93,12 @@ public class CreateTable extends SchemaCommand {
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);
}
}
}
}
changePrimaryKeysToNotNull(data.columns);
data.id = getObjectId();
data.create = create;
data.session = session;
Table table = getSchema().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);
if (!Constants.CLUSTERING_DISABLED
.equals(session.getDatabase().getCluster())) {
throw DbException.getUnsupportedException(
"CLUSTERING && auto-increment columns");
}
}
Sequence seq = c.getSequence();
if (seq != null) {
sequences.add(seq);
}
}
ArrayList<Sequence> sequences = generateSequences(data.columns, data.temporary);
table.setComment(comment);
if (isSessionTemporary) {
if (onCommitDrop) {
......@@ -174,10 +119,7 @@ public class CreateTable extends SchemaCommand {
for (Sequence sequence : sequences) {
table.addSequence(sequence);
}
for (DefineCommand command : constraintCommands) {
command.setTransactional(transactional);
command.update();
}
createConstraints();
if (asQuery != null) {
boolean old = session.isUndoLogEnabled();
try {
......@@ -268,30 +210,6 @@ public class CreateTable extends SchemaCommand {
}
}
/**
* 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;
}
......
......@@ -46,3 +46,43 @@ SELECT * FROM TEST;
DROP TABLE TEST;
> ok
CREATE TABLE TEST(A INT NOT NULL, B INT);
> ok
-- column B may be null
ALTER TABLE TEST ADD (CONSTRAINT PK_B PRIMARY KEY (B));
> exception
ALTER TABLE TEST ADD (CONSTRAINT PK_A PRIMARY KEY (A));
> ok
ALTER TABLE TEST ADD (C INT AUTO_INCREMENT UNIQUE, CONSTRAINT U_B UNIQUE (B), D INT UNIQUE);
> ok
INSERT INTO TEST(A, B, D) VALUES (11, 12, 14);
> update count: 1
SELECT * FROM TEST;
> A B C D
> -- -- - --
> 11 12 1 14
> rows: 1
INSERT INTO TEST VALUES (11, 20, 30, 40);
> exception
INSERT INTO TEST VALUES (10, 12, 30, 40);
> exception
INSERT INTO TEST VALUES (10, 20, 1, 40);
> exception
INSERT INTO TEST VALUES (10, 20, 30, 14);
> exception
INSERT INTO TEST VALUES (10, 20, 30, 40);
> update count: 1
DROP TABLE TEST;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论