提交 b3f996cd authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add optional MySQL-style ON UPDATE to columns

上级 1ca4c405
......@@ -329,9 +329,11 @@ ALTER TABLE [ IF EXISTS ] tableName ALTER COLUMN columnName
| { RESTART WITH long }
| { SELECTIVITY int }
| { SET DEFAULT expression }
| { SET ON UPDATE expression }
| { SET NULL }
| { SET NOT NULL }
| { SET { VISIBLE | INVISIBLE } } }
| { SET { VISIBLE | INVISIBLE } }
| { DROP { DEFAULT | ON UPDATE } } }
","
Changes the data type of a column, rename a column,
change the identity value, or change the selectivity.
......@@ -349,6 +351,8 @@ Selectivity 100 means values are unique, 10 means every distinct value appears 1
SET DEFAULT changes the default value of a column.
SET ON UPDATE changes the value that is set on update if value for this column is not specified in update statement.
SET NULL sets a column to allow NULL. The row may not be part of a primary key.
Single column indexes on this column are dropped.
......@@ -357,6 +361,10 @@ SET NOT NULL sets a column to not allow NULL. Rows may not contains NULL in this
SET INVISIBLE makes the column hidden, i.e. it will not appear in SELECT * results.
SET VISIBLE has the reverse effect.
DROP DEFAULT removes the default value of a column.
DROP ON UPDATE removes the value that is set on update of a column.
This command commits an open transaction in this connection.
","
ALTER TABLE TEST ALTER COLUMN NAME CLOB;
......@@ -2050,13 +2058,17 @@ AES
"Other Grammar","Column Definition","
dataType [ VISIBLE | INVISIBLE ]
[ { DEFAULT expression | AS computedColumnExpression } ] [ [ NOT ] NULL ]
[ { DEFAULT expression | AS computedColumnExpression } ]
[ ON UPDATE expression ] [ [ NOT ] NULL ]
[ { AUTO_INCREMENT | IDENTITY } [ ( startInt [, incrementInt ] ) ] ]
[ SELECTIVITY selectivity ] [ COMMENT expression ]
[ PRIMARY KEY [ HASH ] | UNIQUE ] [ CHECK condition ]
","
Default expressions are used if no explicit value was used when adding a row.
The computed column expression is evaluated and assigned whenever the row changes.
On update column expression is used if row is updated,
at least one column have a new value that is different from its previous value
and value for this column is not set explicitly in update statement.
Identity and auto-increment columns are columns with a sequence as the
default. The column declared as the identity columns is implicitly the
......
......@@ -478,6 +478,11 @@ public interface CommandInterface {
*/
int DROP_SYNONYM = 89;
/**
* The type of a ALTER TABLE ALTER COLUMN SET ON UPDATE statement.
*/
int ALTER_TABLE_ALTER_COLUMN_ON_UPDATE = 90;
/**
* Get command type.
*
......
......@@ -4291,6 +4291,11 @@ public class Parser {
column.setPrimaryKey(true);
column.setAutoIncrement(true, start, increment);
}
if (readIf("ON")) {
read("UPDATE");
Expression onUpdateExpression = readExpression();
column.setOnUpdateExpression(session, onUpdateExpression);
}
if (NullConstraintType.NULL_IS_NOT_ALLOWED == parseNotNullConstraint()) {
column.setNullable(false);
}
......@@ -6205,6 +6210,16 @@ public class Parser {
command.setDefaultExpression(null);
return command;
}
if (readIf("ON")) {
read("UPDATE");
AlterTableAlterColumn command = new AlterTableAlterColumn(session, schema);
command.setTableName(tableName);
command.setIfTableExists(ifTableExists);
command.setOldColumn(column);
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_ON_UPDATE);
command.setDefaultExpression(null);
return command;
}
read("NOT");
read("NULL");
AlterTableAlterColumn command = new AlterTableAlterColumn(
......@@ -6243,11 +6258,14 @@ public class Parser {
Expression defaultExpression = readExpression();
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT);
command.setDefaultExpression(defaultExpression);
} else if (readIf("ON")) {
read("UPDATE");
Expression onUpdateExpression = readExpression();
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_ON_UPDATE);
command.setDefaultExpression(onUpdateExpression);
} else if (readIf("INVISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(false);
} else if (readIf("VISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(true);
......
......@@ -54,6 +54,9 @@ public class AlterTableAlterColumn extends CommandWithColumns {
private Column oldColumn;
private Column newColumn;
private int type;
/**
* Default or on update expression.
*/
private Expression defaultExpression;
private Expression newSelectivity;
private boolean addFirst;
......@@ -147,6 +150,12 @@ public class AlterTableAlterColumn extends CommandWithColumns {
db.updateMeta(session, table);
break;
}
case CommandInterface.ALTER_TABLE_ALTER_COLUMN_ON_UPDATE: {
checkDefaultReferencesTable(table, defaultExpression);
oldColumn.setOnUpdateExpression(session, defaultExpression);
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,
......@@ -551,6 +560,11 @@ public class AlterTableAlterColumn extends CommandWithColumns {
newSelectivity = selectivity;
}
/**
* Set default or on update expression.
*
* @param defaultExpression default or on update expression
*/
public void setDefaultExpression(Expression defaultExpression) {
this.defaultExpression = defaultExpression;
}
......
......@@ -7,6 +7,8 @@ package org.h2.command.dml;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import org.h2.api.ErrorCode;
import org.h2.api.Trigger;
import org.h2.command.CommandInterface;
......@@ -115,20 +117,43 @@ public class Update extends Prepared {
if (condition == null || condition.getBooleanValue(session)) {
Row oldRow = targetTableFilter.get();
Row newRow = table.getTemplateRow();
boolean setOnUpdate = false;
for (int i = 0; i < columnCount; i++) {
Expression newExpr = expressionMap.get(columns[i]);
Column column = table.getColumn(i);
Value newValue;
if (newExpr == null) {
if (column.getOnUpdateExpression() != null) {
setOnUpdate = true;
}
newValue = oldRow.getValue(i);
} else if (newExpr == ValueExpression.getDefault()) {
Column column = table.getColumn(i);
newValue = table.getDefaultValue(session, column);
} else {
Column column = table.getColumn(i);
newValue = column.convert(newExpr.getValue(session));
}
newRow.setValue(i, newValue);
}
if (setOnUpdate) {
setOnUpdate = false;
for (int i = 0; i < columnCount; i++) {
// Use equals here to detect changes from numeric 0 to 0.0 and similar
if (!Objects.equals(oldRow.getValue(i), newRow.getValue(i))) {
setOnUpdate = true;
break;
}
}
if (setOnUpdate) {
for (int i = 0; i < columnCount; i++) {
if (expressionMap.get(columns[i]) == null) {
Column column = table.getColumn(i);
if (column.getOnUpdateExpression() != null) {
newRow.setValue(i, table.getOnUpdateValue(session, column));
}
}
}
}
}
table.validateConvertUpdateSequence(session, newRow);
boolean done = false;
if (table.fireRow()) {
......
......@@ -75,6 +75,7 @@ public class Column {
private int columnId;
private boolean nullable = true;
private Expression defaultExpression;
private Expression onUpdateExpression;
private Expression checkConstraint;
private String checkConstraintSQL;
private String originalSQL;
......@@ -248,6 +249,23 @@ public class Column {
this.defaultExpression = defaultExpression;
}
/**
* Set the on update expression.
*
* @param session the session
* @param onUpdateExpression the on update expression
*/
public void setOnUpdateExpression(Session session, Expression onUpdateExpression) {
// also to test that no column names are used
if (onUpdateExpression != null) {
onUpdateExpression = onUpdateExpression.optimize(session);
if (onUpdateExpression.isConstant()) {
onUpdateExpression = ValueExpression.get(onUpdateExpression.getValue(session));
}
}
this.onUpdateExpression = onUpdateExpression;
}
public int getColumnId() {
return columnId;
}
......@@ -461,11 +479,16 @@ public class Column {
* @param session the session
*/
public void prepareExpression(Session session) {
if (defaultExpression != null) {
computeTableFilter = new TableFilter(session, table, null, false, null, 0,
null);
defaultExpression.mapColumns(computeTableFilter, 0);
defaultExpression = defaultExpression.optimize(session);
if (defaultExpression != null || onUpdateExpression != null) {
computeTableFilter = new TableFilter(session, table, null, false, null, 0, null);
if (defaultExpression != null) {
defaultExpression.mapColumns(computeTableFilter, 0);
defaultExpression = defaultExpression.optimize(session);
}
if (onUpdateExpression != null) {
onUpdateExpression.mapColumns(computeTableFilter, 0);
onUpdateExpression = onUpdateExpression.optimize(session);
}
}
}
......@@ -526,6 +549,12 @@ public class Column {
}
}
}
if (onUpdateExpression != null) {
String sql = onUpdateExpression.getSQL();
if (sql != null) {
buff.append(" ON UPDATE ").append(sql);
}
}
if (!nullable) {
buff.append(" NOT NULL");
}
......@@ -563,6 +592,10 @@ public class Column {
return defaultExpression;
}
public Expression getOnUpdateExpression() {
return onUpdateExpression;
}
public boolean isAutoIncrement() {
return autoIncrement;
}
......@@ -695,6 +728,10 @@ public class Column {
return defaultExpression == null ? null : defaultExpression.getSQL();
}
String getOnUpdateSQL() {
return onUpdateExpression == null ? null : onUpdateExpression.getSQL();
}
int getPrecisionAsInt() {
return MathUtils.convertLongToInt(precision);
}
......@@ -800,6 +837,9 @@ public class Column {
if (isComputed || newColumn.isComputed) {
return false;
}
if (onUpdateExpression != null || newColumn.onUpdateExpression != null) {
return false;
}
return true;
}
......@@ -821,6 +861,7 @@ public class Column {
// columnId is not set
nullable = source.nullable;
defaultExpression = source.defaultExpression;
onUpdateExpression = source.onUpdateExpression;
originalSQL = source.originalSQL;
// autoIncrement, start, increment is not set
convertNullToDefault = source.convertNullToDefault;
......
......@@ -179,7 +179,8 @@ public class MetaTable extends Table {
"SEQUENCE_NAME",
"REMARKS",
"SOURCE_DATA_TYPE SMALLINT",
"COLUMN_TYPE"
"COLUMN_TYPE",
"COLUMN_ON_UPDATE"
);
indexColumnName = "TABLE_NAME";
break;
......@@ -882,7 +883,9 @@ public class MetaTable extends Table {
// SOURCE_DATA_TYPE
null,
// COLUMN_TYPE
c.getCreateSQLWithoutName()
c.getCreateSQLWithoutName(),
// COLUMN_ON_UPDATE
c.getOnUpdateSQL()
);
}
}
......
......@@ -1209,6 +1209,16 @@ public abstract class Table extends SchemaObjectBase {
database.checkWritingAllowed();
}
private static Value getGeneratedValue(Session session, Column column, Expression expression) {
Value v;
if (expression == null) {
v = column.validateConvertUpdateSequence(session, null);
} else {
v = expression.getValue(session);
}
return column.convert(v);
}
/**
* Get or generate a default value for the given column.
*
......@@ -1217,14 +1227,18 @@ public abstract class Table extends SchemaObjectBase {
* @return the value
*/
public Value getDefaultValue(Session session, Column column) {
Expression defaultExpr = column.getDefaultExpression();
Value v;
if (defaultExpr == null) {
v = column.validateConvertUpdateSequence(session, null);
} else {
v = defaultExpr.getValue(session);
}
return column.convert(v);
return getGeneratedValue(session, column, column.getDefaultExpression());
}
/**
* Generates on update value for the given column.
*
* @param session the session
* @param column the column
* @return the value
*/
public Value getOnUpdateValue(Session session, Column column) {
return getGeneratedValue(session, column, column.getOnUpdateExpression());
}
@Override
......
......@@ -86,6 +86,7 @@ public class TestScript extends TestBase {
testScript("information_schema.sql");
testScript("joins.sql");
testScript("altertable-index-reuse.sql");
testScript("default-and-on_update.sql");
testScript("query-optimisations.sql");
String decimal2;
if (SysProperties.BIG_DECIMAL_IS_DECIMAL) {
......@@ -150,7 +151,7 @@ public class TestScript extends TestBase {
"parsedatetime", "quarter", "second", "truncate", "week", "year", "date_trunc" }) {
testScript("functions/timeanddate/" + s + ".sql");
}
deleteDb("script");
System.out.flush();
}
......
......@@ -9,13 +9,13 @@ create memory table orders ( orderid varchar(10), name varchar(20), customer_id
> ok
select * from information_schema.columns where table_name = 'ORDERS';
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- -------------------
> SCRIPT PUBLIC ORDERS COMPLETED 4 null NO 3 1 1 1 10 0 Unicode OFF DECIMAL 0 FALSE 50 null null NUMERIC(1) NOT NULL
> SCRIPT PUBLIC ORDERS CUSTOMER_ID 3 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10)
> SCRIPT PUBLIC ORDERS NAME 2 null YES 12 20 20 20 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(20)
> SCRIPT PUBLIC ORDERS ORDERID 1 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10)
> SCRIPT PUBLIC ORDERS VERIFIED 5 null YES 3 1 1 1 10 0 Unicode OFF DECIMAL 1 FALSE 50 null null NUMERIC(1)
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE COLUMN_ON_UPDATE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- ------------------- ----------------
> SCRIPT PUBLIC ORDERS COMPLETED 4 null NO 3 1 1 1 10 0 Unicode OFF DECIMAL 0 FALSE 50 null null NUMERIC(1) NOT NULL null
> SCRIPT PUBLIC ORDERS CUSTOMER_ID 3 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10) null
> SCRIPT PUBLIC ORDERS NAME 2 null YES 12 20 20 20 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(20) null
> SCRIPT PUBLIC ORDERS ORDERID 1 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10) null
> SCRIPT PUBLIC ORDERS VERIFIED 5 null YES 3 1 1 1 10 0 Unicode OFF DECIMAL 1 FALSE 50 null null NUMERIC(1) null
> rows: 5
drop table orders;
......
......@@ -9,13 +9,13 @@ create memory table orders ( orderid varchar(10), name varchar(20), customer_id
> ok
select * from information_schema.columns where table_name = 'ORDERS';
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- -------------------
> SCRIPT PUBLIC ORDERS COMPLETED 4 null NO 2 1 1 1 10 0 Unicode OFF NUMERIC 0 FALSE 50 null null NUMERIC(1) NOT NULL
> SCRIPT PUBLIC ORDERS CUSTOMER_ID 3 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10)
> SCRIPT PUBLIC ORDERS NAME 2 null YES 12 20 20 20 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(20)
> SCRIPT PUBLIC ORDERS ORDERID 1 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10)
> SCRIPT PUBLIC ORDERS VERIFIED 5 null YES 2 1 1 1 10 0 Unicode OFF NUMERIC 1 FALSE 50 null null NUMERIC(1)
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE COLUMN_ON_UPDATE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- ------------------- ----------------
> SCRIPT PUBLIC ORDERS COMPLETED 4 null NO 2 1 1 1 10 0 Unicode OFF NUMERIC 0 FALSE 50 null null NUMERIC(1) NOT NULL null
> SCRIPT PUBLIC ORDERS CUSTOMER_ID 3 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10) null
> SCRIPT PUBLIC ORDERS NAME 2 null YES 12 20 20 20 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(20) null
> SCRIPT PUBLIC ORDERS ORDERID 1 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10) null
> SCRIPT PUBLIC ORDERS VERIFIED 5 null YES 2 1 1 1 10 0 Unicode OFF NUMERIC 1 FALSE 50 null null NUMERIC(1) null
> rows: 5
drop table orders;
......
-- 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
--
CREATE SEQUENCE SEQ;
> ok
CREATE TABLE TEST(ID INT PRIMARY KEY, V INT DEFAULT NEXT VALUE FOR SEQ ON UPDATE 1000 * NEXT VALUE FOR SEQ);
> ok
INSERT INTO TEST(ID) VALUES (1), (2);
> update count: 2
SELECT * FROM TEST ORDER BY ID;
> ID V
> -- -
> 1 1
> 2 2
> rows (ordered): 2
UPDATE TEST SET ID = 3 WHERE ID = 2;
> update count: 1
SELECT * FROM TEST ORDER BY ID;
> ID V
> -- ----
> 1 1
> 3 3000
> rows (ordered): 2
UPDATE TEST SET V = 3 WHERE ID = 3;
> update count: 1
SELECT * FROM TEST ORDER BY ID;
> ID V
> -- -
> 1 1
> 3 3
> rows (ordered): 2
ALTER TABLE TEST ADD V2 TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
> ok
UPDATE TEST SET V = 4 WHERE ID = 3;
> update count: 1
SELECT ID, V, LENGTH(V2) > 18 AS L FROM TEST ORDER BY ID;
> ID V L
> -- - ----
> 1 1 null
> 3 4 TRUE
> rows (ordered): 2
UPDATE TEST SET V = 1 WHERE V = 1;
> update count: 1
SELECT ID, V, LENGTH(V2) > 18 AS L FROM TEST ORDER BY ID;
> ID V L
> -- - ----
> 1 1 null
> 3 4 TRUE
> rows (ordered): 2
MERGE INTO TEST(ID, V) KEY(ID) VALUES (1, 1);
> update count: 1
SELECT ID, V, LENGTH(V2) > 18 AS L FROM TEST ORDER BY ID;
> ID V L
> -- - ----
> 1 1 null
> 3 4 TRUE
> rows (ordered): 2
MERGE INTO TEST(ID, V) KEY(ID) VALUES (1, 2);
> update count: 1
SELECT ID, V, LENGTH(V2) > 18 AS L FROM TEST ORDER BY ID;
> ID V L
> -- - ----
> 1 2 TRUE
> 3 4 TRUE
> rows (ordered): 2
ALTER TABLE TEST ALTER COLUMN V SET ON UPDATE NULL;
> ok
SELECT COLUMN_NAME, COLUMN_DEFAULT, COLUMN_ON_UPDATE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TEST' ORDER BY COLUMN_NAME;
> COLUMN_NAME COLUMN_DEFAULT COLUMN_ON_UPDATE
> ----------- --------------------------- -------------------
> ID null null
> V (NEXT VALUE FOR PUBLIC.SEQ) NULL
> V2 null CURRENT_TIMESTAMP()
> rows (ordered): 3
ALTER TABLE TEST ALTER COLUMN V DROP ON UPDATE;
> ok
SELECT COLUMN_NAME, COLUMN_DEFAULT, COLUMN_ON_UPDATE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TEST' ORDER BY COLUMN_NAME;
> COLUMN_NAME COLUMN_DEFAULT COLUMN_ON_UPDATE
> ----------- --------------------------- -------------------
> ID null null
> V (NEXT VALUE FOR PUBLIC.SEQ) null
> V2 null CURRENT_TIMESTAMP()
> rows (ordered): 3
DROP TABLE TEST;
> ok
DROP SEQUENCE SEQ;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论