提交 6391e991 authored 作者: andrei's avatar andrei

Merge remote-tracking branch 'h2database/master' into non_blocking

...@@ -284,7 +284,8 @@ ALTER SEQUENCE SEQ_ID RESTART WITH 1000 ...@@ -284,7 +284,8 @@ ALTER SEQUENCE SEQ_ID RESTART WITH 1000
"Commands (DDL)","ALTER TABLE ADD"," "Commands (DDL)","ALTER TABLE ADD","
ALTER TABLE [ IF EXISTS ] tableName ADD [ COLUMN ] 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 ] [ { { BEFORE | AFTER } columnName } | FIRST ]
"," ","
Adds a new column to a table. Adds a new column to a table.
......
...@@ -48,7 +48,7 @@ import org.h2.util.New; ...@@ -48,7 +48,7 @@ import org.h2.util.New;
* ALTER TABLE ALTER COLUMN SET INVISIBLE, * ALTER TABLE ALTER COLUMN SET INVISIBLE,
* ALTER TABLE DROP COLUMN * ALTER TABLE DROP COLUMN
*/ */
public class AlterTableAlterColumn extends SchemaCommand { public class AlterTableAlterColumn extends CommandWithColumns {
private String tableName; private String tableName;
private Column oldColumn; private Column oldColumn;
...@@ -175,18 +175,15 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -175,18 +175,15 @@ public class AlterTableAlterColumn extends SchemaCommand {
} }
case CommandInterface.ALTER_TABLE_ADD_COLUMN: { case CommandInterface.ALTER_TABLE_ADD_COLUMN: {
// ifNotExists only supported for single column add // 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())) { table.doesColumnExist(columnsToAdd.get(0).getName())) {
break; break;
} }
for (Column column : columnsToAdd) { ArrayList<Sequence> sequences = generateSequences(columnsToAdd, false);
if (column.isAutoIncrement()) { if (columnsToAdd != null) {
int objId = getObjectId(); changePrimaryKeysToNotNull(columnsToAdd);
column.convertAutoIncrementToSequence(session, getSchema(), objId,
table.isTemporary());
}
} }
copyData(table); copyData(table, sequences, true);
break; break;
} }
case CommandInterface.ALTER_TABLE_DROP_COLUMN: { case CommandInterface.ALTER_TABLE_DROP_COLUMN: {
...@@ -261,6 +258,10 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -261,6 +258,10 @@ public class AlterTableAlterColumn extends SchemaCommand {
} }
private void copyData(Table table) { private void copyData(Table table) {
copyData(table, null, false);
}
private void copyData(Table table, ArrayList<Sequence> sequences, boolean createConstraints) {
if (table.isTemporary()) { if (table.isTemporary()) {
throw DbException.getUnsupportedException("TEMP TABLE"); throw DbException.getUnsupportedException("TEMP TABLE");
} }
...@@ -270,6 +271,11 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -270,6 +271,11 @@ public class AlterTableAlterColumn extends SchemaCommand {
Column[] columns = table.getColumns(); Column[] columns = table.getColumns();
ArrayList<Column> newColumns = New.arrayList(); ArrayList<Column> newColumns = New.arrayList();
Table newTable = cloneTableStructure(table, columns, db, tempName, newColumns); Table newTable = cloneTableStructure(table, columns, db, tempName, newColumns);
if (sequences != null) {
for (Sequence sequence : sequences) {
table.addSequence(sequence);
}
}
try { try {
// check if a view would become invalid // check if a view would become invalid
// (because the column to drop is referenced or so) // (because the column to drop is referenced or so)
...@@ -308,6 +314,9 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -308,6 +314,9 @@ public class AlterTableAlterColumn extends SchemaCommand {
db.renameSchemaObject(session, so, name); db.renameSchemaObject(session, so, name);
} }
} }
if (createConstraints) {
createConstraints();
}
for (TableView view : dependentViews) { for (TableView view : dependentViews) {
String sql = view.getCreateSQL(true, true); String sql = view.getCreateSQL(true, true);
execute(sql, true); execute(sql, true);
...@@ -345,8 +354,10 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -345,8 +354,10 @@ public class AlterTableAlterColumn extends SchemaCommand {
} else { } else {
position = columns.length; position = columns.length;
} }
for (Column column : columnsToAdd) { if (columnsToAdd != null) {
newColumns.add(position++, column); for (Column column : columnsToAdd) {
newColumns.add(position++, column);
}
} }
} else if (type == CommandInterface.ALTER_TABLE_ALTER_COLUMN_CHANGE_TYPE) { } else if (type == CommandInterface.ALTER_TABLE_ALTER_COLUMN_CHANGE_TYPE) {
int position = oldColumn.getColumnId(); int position = oldColumn.getColumnId();
...@@ -379,7 +390,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -379,7 +390,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
columnList.append(", "); columnList.append(", ");
} }
if (type == CommandInterface.ALTER_TABLE_ADD_COLUMN && if (type == CommandInterface.ALTER_TABLE_ADD_COLUMN &&
columnsToAdd.contains(nc)) { columnsToAdd != null && columnsToAdd.contains(nc)) {
Expression def = nc.getDefaultExpression(); Expression def = nc.getDefaultExpression();
columnList.append(def == null ? "NULL" : def.getSQL()); columnList.append(def == null ? "NULL" : def.getSQL());
} else { } else {
...@@ -557,8 +568,12 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -557,8 +568,12 @@ public class AlterTableAlterColumn extends SchemaCommand {
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
} }
public void setNewColumns(ArrayList<Column> columnsToAdd) { @Override
this.columnsToAdd = columnsToAdd; public void addColumn(Column column) {
if (columnsToAdd == null) {
columnsToAdd = New.arrayList();
}
columnsToAdd.add(column);
} }
public void setColumnsToRemove(ArrayList<Column> columnsToRemove) { 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() {
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() {
if (constraintCommands == null) {
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; ...@@ -11,7 +11,6 @@ import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.command.dml.Insert; import org.h2.command.dml.Insert;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.DbObject; import org.h2.engine.DbObject;
import org.h2.engine.Session; import org.h2.engine.Session;
...@@ -21,10 +20,8 @@ import org.h2.message.DbException; ...@@ -21,10 +20,8 @@ import org.h2.message.DbException;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.schema.Sequence; import org.h2.schema.Sequence;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.util.ColumnNamer; import org.h2.util.ColumnNamer;
import org.h2.util.New;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -32,11 +29,9 @@ import org.h2.value.Value; ...@@ -32,11 +29,9 @@ import org.h2.value.Value;
* This class represents the statement * This class represents the statement
* CREATE TABLE * CREATE TABLE
*/ */
public class CreateTable extends SchemaCommand { public class CreateTable extends CommandWithColumns {
private final CreateTableData data = new CreateTableData(); private final CreateTableData data = new CreateTableData();
private final ArrayList<DefineCommand> constraintCommands = New.arrayList();
private IndexColumn[] pkColumns;
private boolean ifNotExists; private boolean ifNotExists;
private boolean onCommitDrop; private boolean onCommitDrop;
private boolean onCommitTruncate; private boolean onCommitTruncate;
...@@ -62,38 +57,11 @@ public class CreateTable extends SchemaCommand { ...@@ -62,38 +57,11 @@ public class CreateTable extends SchemaCommand {
data.tableName = tableName; data.tableName = tableName;
} }
/** @Override
* Add a column to this table.
*
* @param column the column to add
*/
public void addColumn(Column column) { public void addColumn(Column column) {
data.columns.add(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) { public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
} }
...@@ -125,35 +93,12 @@ public class CreateTable extends SchemaCommand { ...@@ -125,35 +93,12 @@ public class CreateTable extends SchemaCommand {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
} }
} }
if (pkColumns != null) { changePrimaryKeysToNotNull(data.columns);
for (Column c : data.columns) {
for (IndexColumn idxCol : pkColumns) {
if (c.getName().equals(idxCol.columnName)) {
c.setNullable(false);
}
}
}
}
data.id = getObjectId(); data.id = getObjectId();
data.create = create; data.create = create;
data.session = session; data.session = session;
Table table = getSchema().createTable(data); Table table = getSchema().createTable(data);
ArrayList<Sequence> sequences = New.arrayList(); ArrayList<Sequence> sequences = generateSequences(data.columns, data.temporary);
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);
}
}
table.setComment(comment); table.setComment(comment);
if (isSessionTemporary) { if (isSessionTemporary) {
if (onCommitDrop) { if (onCommitDrop) {
...@@ -174,10 +119,7 @@ public class CreateTable extends SchemaCommand { ...@@ -174,10 +119,7 @@ public class CreateTable extends SchemaCommand {
for (Sequence sequence : sequences) { for (Sequence sequence : sequences) {
table.addSequence(sequence); table.addSequence(sequence);
} }
for (DefineCommand command : constraintCommands) { createConstraints();
command.setTransactional(transactional);
command.update();
}
if (asQuery != null) { if (asQuery != null) {
boolean old = session.isUndoLogEnabled(); boolean old = session.isUndoLogEnabled();
try { try {
...@@ -268,30 +210,6 @@ public class CreateTable extends SchemaCommand { ...@@ -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) { public void setPersistIndexes(boolean persistIndexes) {
data.persistIndexes = persistIndexes; data.persistIndexes = persistIndexes;
} }
......
...@@ -39,7 +39,23 @@ public abstract class Constraint extends SchemaObjectBase implements ...@@ -39,7 +39,23 @@ public abstract class Constraint extends SchemaObjectBase implements
/** /**
* The constraint type for referential constraints. * The constraint type for referential constraints.
*/ */
REFERENTIAL REFERENTIAL;
/**
* Get standard SQL type name.
*
* @return standard SQL type name
*/
public String getSqlName() {
if (this == Constraint.Type.PRIMARY_KEY) {
return "PRIMARY KEY";
}
if (this == Constraint.Type.REFERENTIAL) {
return "FOREIGN KEY";
}
return name();
}
} }
/** /**
......
...@@ -24,5 +24,21 @@ public enum ConstraintActionType { ...@@ -24,5 +24,21 @@ public enum ConstraintActionType {
/** /**
* The action is to set the value to NULL. * The action is to set the value to NULL.
*/ */
SET_NULL SET_NULL;
/**
* Get standard SQL type name.
*
* @return standard SQL type name
*/
public String getSqlName() {
if (this == ConstraintActionType.SET_DEFAULT) {
return "SET DEFAULT";
}
if (this == SET_NULL) {
return "SET NULL";
}
return name();
}
} }
\ No newline at end of file
...@@ -54,22 +54,6 @@ public class ConstraintReferential extends Constraint { ...@@ -54,22 +54,6 @@ public class ConstraintReferential extends Constraint {
return Constraint.Type.REFERENTIAL; return Constraint.Type.REFERENTIAL;
} }
private static void appendAction(StatementBuilder buff, ConstraintActionType action) {
switch (action) {
case CASCADE:
buff.append("CASCADE");
break;
case SET_DEFAULT:
buff.append("SET DEFAULT");
break;
case SET_NULL:
buff.append("SET NULL");
break;
default:
DbException.throwInternalError("action=" + action);
}
}
/** /**
* Create the SQL statement of this object so a copy of the table can be * Create the SQL statement of this object so a copy of the table can be
* made. * made.
...@@ -135,12 +119,10 @@ public class ConstraintReferential extends Constraint { ...@@ -135,12 +119,10 @@ public class ConstraintReferential extends Constraint {
buff.append(" INDEX ").append(refIndex.getSQL()); buff.append(" INDEX ").append(refIndex.getSQL());
} }
if (deleteAction != ConstraintActionType.RESTRICT) { if (deleteAction != ConstraintActionType.RESTRICT) {
buff.append(" ON DELETE "); buff.append(" ON DELETE ").append(deleteAction.getSqlName());
appendAction(buff, deleteAction);
} }
if (updateAction != ConstraintActionType.RESTRICT) { if (updateAction != ConstraintActionType.RESTRICT) {
buff.append(" ON UPDATE "); buff.append(" ON UPDATE ").append(updateAction.getSqlName());
appendAction(buff, updateAction);
} }
return buff.append(" NOCHECK").toString(); return buff.append(" NOCHECK").toString();
} }
......
...@@ -54,7 +54,7 @@ public class ConstraintUnique extends Constraint { ...@@ -54,7 +54,7 @@ public class ConstraintUnique extends Constraint {
if (comment != null) { if (comment != null) {
buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment)); buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
} }
buff.append(' ').append(getTypeName()).append('('); buff.append(' ').append(getConstraintType().getSqlName()).append('(');
for (IndexColumn c : columns) { for (IndexColumn c : columns) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(Parser.quoteIdentifier(c.column.getName())); buff.append(Parser.quoteIdentifier(c.column.getName()));
...@@ -66,13 +66,6 @@ public class ConstraintUnique extends Constraint { ...@@ -66,13 +66,6 @@ public class ConstraintUnique extends Constraint {
return buff.toString(); return buff.toString();
} }
private String getTypeName() {
if (primaryKey) {
return "PRIMARY KEY";
}
return "UNIQUE";
}
@Override @Override
public String getCreateSQLWithoutIndexes() { public String getCreateSQLWithoutIndexes() {
return getCreateSQLForCopy(table, getSQL(), false); return getCreateSQLForCopy(table, getSQL(), false);
......
...@@ -110,8 +110,10 @@ public class MetaTable extends Table { ...@@ -110,8 +110,10 @@ public class MetaTable extends Table {
private static final int SESSION_STATE = 27; private static final int SESSION_STATE = 27;
private static final int QUERY_STATISTICS = 28; private static final int QUERY_STATISTICS = 28;
private static final int SYNONYMS = 29; private static final int SYNONYMS = 29;
private static final int KEY_COLUMN_USAGE = 30; private static final int TABLE_CONSTRAINTS = 30;
private static final int META_TABLE_TYPE_COUNT = KEY_COLUMN_USAGE + 1; private static final int KEY_COLUMN_USAGE = 31;
private static final int REFERENTIAL_CONSTRAINTS = 32;
private static final int META_TABLE_TYPE_COUNT = REFERENTIAL_CONSTRAINTS + 1;
private final int type; private final int type;
private final int indexColumn; private final int indexColumn;
...@@ -558,6 +560,22 @@ public class MetaTable extends Table { ...@@ -558,6 +560,22 @@ public class MetaTable extends Table {
indexColumnName = "SYNONYM_NAME"; indexColumnName = "SYNONYM_NAME";
break; break;
} }
case TABLE_CONSTRAINTS: {
setObjectName("TABLE_CONSTRAINTS");
cols = createColumns(
"CONSTRAINT_CATALOG",
"CONSTRAINT_SCHEMA",
"CONSTRAINT_NAME",
"CONSTRAINT_TYPE",
"TABLE_CATALOG",
"TABLE_SCHEMA",
"TABLE_NAME",
"IS_DEFERRABLE",
"INITIALLY_DEFERRED"
);
indexColumnName = "TABLE_NAME";
break;
}
case KEY_COLUMN_USAGE: { case KEY_COLUMN_USAGE: {
setObjectName("KEY_COLUMN_USAGE"); setObjectName("KEY_COLUMN_USAGE");
cols = createColumns( cols = createColumns(
...@@ -574,6 +592,21 @@ public class MetaTable extends Table { ...@@ -574,6 +592,21 @@ public class MetaTable extends Table {
indexColumnName = "TABLE_NAME"; indexColumnName = "TABLE_NAME";
break; break;
} }
case REFERENTIAL_CONSTRAINTS: {
setObjectName("REFERENTIAL_CONSTRAINTS");
cols = createColumns(
"CONSTRAINT_CATALOG",
"CONSTRAINT_SCHEMA",
"CONSTRAINT_NAME",
"UNIQUE_CONSTRAINT_CATALOG",
"UNIQUE_CONSTRAINT_SCHEMA",
"UNIQUE_CONSTRAINT_NAME",
"MATCH_OPTION",
"UPDATE_RULE",
"DELETE_RULE"
);
break;
}
default: default:
throw DbException.throwInternalError("type="+type); throw DbException.throwInternalError("type="+type);
} }
...@@ -1924,9 +1957,43 @@ public class MetaTable extends Table { ...@@ -1924,9 +1957,43 @@ public class MetaTable extends Table {
} }
break; break;
} }
case TABLE_CONSTRAINTS: {
for (SchemaObject obj : database.getAllSchemaObjects(DbObject.CONSTRAINT)) {
Constraint constraint = (Constraint) obj;
Constraint.Type constraintType = constraint.getConstraintType();
Table table = constraint.getTable();
if (hideTable(table, session)) {
continue;
}
String tableName = identifier(table.getName());
if (!checkIndex(session, tableName, indexFrom, indexTo)) {
continue;
}
add(rows,
// CONSTRAINT_CATALOG
catalog,
// CONSTRAINT_SCHEMA
identifier(constraint.getSchema().getName()),
// CONSTRAINT_NAME
identifier(constraint.getName()),
// CONSTRAINT_TYPE
constraintType.getSqlName(),
// TABLE_CATALOG
catalog,
// TABLE_SCHEMA
identifier(table.getSchema().getName()),
// TABLE_NAME
tableName,
// IS_DEFERRABLE
"NO",
// INITIALLY_DEFERRED
"NO"
);
}
break;
}
case KEY_COLUMN_USAGE: { case KEY_COLUMN_USAGE: {
for (SchemaObject obj : database.getAllSchemaObjects( for (SchemaObject obj : database.getAllSchemaObjects(DbObject.CONSTRAINT)) {
DbObject.CONSTRAINT)) {
Constraint constraint = (Constraint) obj; Constraint constraint = (Constraint) obj;
Constraint.Type constraintType = constraint.getConstraintType(); Constraint.Type constraintType = constraint.getConstraintType();
IndexColumn[] indexColumns = null; IndexColumn[] indexColumns = null;
...@@ -1947,9 +2014,31 @@ public class MetaTable extends Table { ...@@ -1947,9 +2014,31 @@ public class MetaTable extends Table {
if (indexColumns == null) { if (indexColumns == null) {
continue; continue;
} }
ConstraintUnique referenced;
if (constraintType == Constraint.Type.REFERENTIAL) {
referenced = lookupUniqueForReferential((ConstraintReferential) constraint);
} else {
referenced = null;
}
for (int i = 0; i < indexColumns.length; i++) { for (int i = 0; i < indexColumns.length; i++) {
IndexColumn indexColumn = indexColumns[i]; IndexColumn indexColumn = indexColumns[i];
String ordinalPosition = Integer.toString(i + 1); String ordinalPosition = Integer.toString(i + 1);
String positionInUniqueConstraint;
if (constraintType == Constraint.Type.REFERENTIAL) {
positionInUniqueConstraint = ordinalPosition;
if (referenced != null) {
Column c = ((ConstraintReferential) constraint).getRefColumns()[i].column;
IndexColumn[] refColumns = referenced.getColumns();
for (int j = 0; j < refColumns.length; j++) {
if (refColumns[j].column.equals(c)) {
positionInUniqueConstraint = Integer.toString(j + 1);
break;
}
}
}
} else {
positionInUniqueConstraint = null;
}
add(rows, add(rows,
// CONSTRAINT_CATALOG // CONSTRAINT_CATALOG
catalog, catalog,
...@@ -1968,9 +2057,49 @@ public class MetaTable extends Table { ...@@ -1968,9 +2057,49 @@ public class MetaTable extends Table {
// ORDINAL_POSITION // ORDINAL_POSITION
ordinalPosition, ordinalPosition,
// POSITION_IN_UNIQUE_CONSTRAINT // POSITION_IN_UNIQUE_CONSTRAINT
(constraintType == Constraint.Type.REFERENTIAL ? ordinalPosition : null) positionInUniqueConstraint
); );
}
}
break;
}
case REFERENTIAL_CONSTRAINTS: {
for (SchemaObject obj : database.getAllSchemaObjects(DbObject.CONSTRAINT)) {
if (((Constraint) obj).getConstraintType() != Constraint.Type.REFERENTIAL) {
continue;
} }
ConstraintReferential constraint = (ConstraintReferential) obj;
Table table = constraint.getTable();
if (hideTable(table, session)) {
continue;
}
// Should be referenced unique constraint, but H2 uses indexes instead.
// So try to find matching unique constraint first and there is no such
// constraint use index name to return something.
SchemaObject unique = lookupUniqueForReferential(constraint);
if (unique == null) {
unique = constraint.getUniqueIndex();
}
add(rows,
// CONSTRAINT_CATALOG
catalog,
// CONSTRAINT_SCHEMA
identifier(constraint.getSchema().getName()),
// CONSTRAINT_NAME
identifier(constraint.getName()),
// UNIQUE_CONSTRAINT_CATALOG
catalog,
// UNIQUE_CONSTRAINT_SCHEMA
identifier(unique.getSchema().getName()),
// UNIQUE_CONSTRAINT_NAME
unique.getName(),
// MATCH_OPTION
"NONE",
// UPDATE_RULE
constraint.getUpdateAction().getSqlName(),
// DELETE_RULE
constraint.getDeleteAction().getSqlName()
);
} }
break; break;
} }
...@@ -1995,6 +2124,19 @@ public class MetaTable extends Table { ...@@ -1995,6 +2124,19 @@ public class MetaTable extends Table {
} }
} }
private static ConstraintUnique lookupUniqueForReferential(ConstraintReferential referential) {
Table table = referential.getRefTable();
for (Constraint c : table.getConstraints()) {
if (c.getConstraintType() == Constraint.Type.UNIQUE) {
ConstraintUnique unique = (ConstraintUnique) c;
if (unique.getReferencedColumns(table).equals(referential.getReferencedColumns(table))) {
return unique;
}
}
}
return null;
}
@Override @Override
public void removeRow(Session session, Row row) { public void removeRow(Session session, Row row) {
throw DbException.getUnsupportedException("META"); throw DbException.getUnsupportedException("META");
......
...@@ -1090,6 +1090,8 @@ public class TestMetaData extends TestBase { ...@@ -1090,6 +1090,8 @@ public class TestMetaData extends TestBase {
rs.next(); rs.next();
assertEquals("QUERY_STATISTICS", rs.getString("TABLE_NAME")); assertEquals("QUERY_STATISTICS", rs.getString("TABLE_NAME"));
rs.next(); rs.next();
assertEquals("REFERENTIAL_CONSTRAINTS", rs.getString("TABLE_NAME"));
rs.next();
assertEquals("RIGHTS", rs.getString("TABLE_NAME")); assertEquals("RIGHTS", rs.getString("TABLE_NAME"));
rs.next(); rs.next();
assertEquals("ROLES", rs.getString("TABLE_NAME")); assertEquals("ROLES", rs.getString("TABLE_NAME"));
...@@ -1108,6 +1110,8 @@ public class TestMetaData extends TestBase { ...@@ -1108,6 +1110,8 @@ public class TestMetaData extends TestBase {
rs.next(); rs.next();
assertEquals("TABLES", rs.getString("TABLE_NAME")); assertEquals("TABLES", rs.getString("TABLE_NAME"));
rs.next(); rs.next();
assertEquals("TABLE_CONSTRAINTS", rs.getString("TABLE_NAME"));
rs.next();
assertEquals("TABLE_PRIVILEGES", rs.getString("TABLE_NAME")); assertEquals("TABLE_PRIVILEGES", rs.getString("TABLE_NAME"));
rs.next(); rs.next();
assertEquals("TABLE_TYPES", rs.getString("TABLE_NAME")); assertEquals("TABLE_TYPES", rs.getString("TABLE_NAME"));
......
...@@ -46,3 +46,43 @@ SELECT * FROM TEST; ...@@ -46,3 +46,43 @@ SELECT * FROM TEST;
DROP TABLE TEST; DROP TABLE TEST;
> ok > 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
...@@ -15,9 +15,32 @@ ALTER TABLE T1 ADD CONSTRAINT U_1 UNIQUE(C3, C4); ...@@ -15,9 +15,32 @@ ALTER TABLE T1 ADD CONSTRAINT U_1 UNIQUE(C3, C4);
CREATE TABLE T2(C1 INT, C2 INT, C3 INT, C4 INT); CREATE TABLE T2(C1 INT, C2 INT, C3 INT, C4 INT);
> ok > ok
ALTER TABLE T2 ADD CONSTRAINT FK_1 FOREIGN KEY (C3, C4) REFERENCES T1(C1, C3); ALTER TABLE T2 ADD CONSTRAINT FK_1 FOREIGN KEY (C3, C4) REFERENCES T1(C1, C3) ON DELETE SET NULL;
> ok > ok
ALTER TABLE T2 ADD CONSTRAINT FK_2 FOREIGN KEY (C3, C4) REFERENCES T1(C4, C3) ON UPDATE CASCADE ON DELETE SET DEFAULT;
> ok
ALTER TABLE T2 ADD CONSTRAINT CH_1 CHECK C4 > 0;
> ok
SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS LIMIT 0;
> CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME IS_DEFERRABLE INITIALLY_DEFERRED
> ------------------ ----------------- --------------- --------------- ------------- ------------ ---------- ------------- ------------------
> rows: 0
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, IS_DEFERRABLE, INITIALLY_DEFERRED FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_CATALOG = DATABASE() AND CONSTRAINT_SCHEMA = SCHEMA() AND TABLE_CATALOG = DATABASE() AND TABLE_SCHEMA = SCHEMA()
ORDER BY TABLE_NAME, CONSTRAINT_NAME;
> CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_NAME IS_DEFERRABLE INITIALLY_DEFERRED
> --------------- --------------- ---------- ------------- ------------------
> PK_1 PRIMARY KEY T1 NO NO
> U_1 UNIQUE T1 NO NO
> CH_1 CHECK T2 NO NO
> FK_1 FOREIGN KEY T2 NO NO
> FK_2 FOREIGN KEY T2 NO NO
> rows (ordered): 5
SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE LIMIT 0; SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE LIMIT 0;
> CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT > CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT
> ------------------ ----------------- --------------- ------------- ------------ ---------- ----------- ---------------- ----------------------------- > ------------------ ----------------- --------------- ------------- ------------ ---------- ----------- ---------------- -----------------------------
...@@ -34,7 +57,34 @@ SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_U ...@@ -34,7 +57,34 @@ SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_U
> U_1 T1 C4 2 null > U_1 T1 C4 2 null
> FK_1 T2 C3 1 1 > FK_1 T2 C3 1 1
> FK_1 T2 C4 2 2 > FK_1 T2 C4 2 2
> rows (ordered): 6 > FK_2 T2 C3 1 2
> FK_2 T2 C4 2 1
> rows (ordered): 8
SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS LIMIT 0;
> CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME UNIQUE_CONSTRAINT_CATALOG UNIQUE_CONSTRAINT_SCHEMA UNIQUE_CONSTRAINT_NAME MATCH_OPTION UPDATE_RULE DELETE_RULE
> ------------------ ----------------- --------------- ------------------------- ------------------------ ---------------------- ------------ ----------- -----------
> rows: 0
-- H2 may return name of the index istead of name of the referenced constraint as UNIQUE_CONSTRAINT_NAME
SELECT CONSTRAINT_NAME, SUBSTRING(UNIQUE_CONSTRAINT_NAME, 0, 11) AS UCN_PART, MATCH_OPTION, UPDATE_RULE, DELETE_RULE FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
WHERE CONSTRAINT_CATALOG = DATABASE() AND CONSTRAINT_SCHEMA = SCHEMA() AND UNIQUE_CONSTRAINT_CATALOG = DATABASE() AND UNIQUE_CONSTRAINT_SCHEMA = SCHEMA()
ORDER BY CONSTRAINT_NAME, UNIQUE_CONSTRAINT_NAME;
> CONSTRAINT_NAME UCN_PART MATCH_OPTION UPDATE_RULE DELETE_RULE
> --------------- ----------- ------------ ----------- -----------
> FK_1 FK_1_INDEX_ NONE RESTRICT SET NULL
> FK_2 U_1 NONE CASCADE SET DEFAULT
> rows (ordered): 2
SELECT U1.TABLE_NAME T1, U1.COLUMN_NAME C1, U2.TABLE_NAME T2, U2.COLUMN_NAME C2
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE U1 JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON U1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U2 ON RC.UNIQUE_CONSTRAINT_NAME = U2.CONSTRAINT_NAME AND U1.POSITION_IN_UNIQUE_CONSTRAINT = U2.ORDINAL_POSITION
WHERE U1.CONSTRAINT_NAME = 'FK_2' ORDER BY U1.COLUMN_NAME;
> T1 C1 T2 C2
> -- -- -- --
> T2 C3 T1 C4
> T2 C4 T1 C3
> rows (ordered): 2
DROP TABLE T2; DROP TABLE T2;
> ok > ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论