提交 f47004ee authored 作者: noelgrandin's avatar noelgrandin

Issue 520: Add support for sequence max value, min value and cycle, patch by Daniel Gredler.

上级 c7687784
...@@ -208,8 +208,12 @@ ALTER SCHEMA TEST RENAME TO PRODUCTION ...@@ -208,8 +208,12 @@ ALTER SCHEMA TEST RENAME TO PRODUCTION
"Commands (DDL)","ALTER SEQUENCE"," "Commands (DDL)","ALTER SEQUENCE","
ALTER SEQUENCE sequenceName [ RESTART WITH long ] [ INCREMENT BY long ] ALTER SEQUENCE sequenceName [ RESTART WITH long ] [ INCREMENT BY long ]
[ MINVALUE long | NOMINVALUE | NO MINVALUE ]
[ MAXVALUE long | NOMAXVALUE | NO MAXVALUE ]
[ CYCLE long | NOCYCLE | NO CYCLE ]
[ CACHE long | NOCACHE | NO CACHE ]
"," ","
Changes the next value and the increment of a sequence. Changes the parameters of a sequence.
This command does not commit the current transaction; however the new value is used by other This command does not commit the current transaction; however the new value is used by other
transactions immediately, and rolling back this command has no effect. transactions immediately, and rolling back this command has no effect.
"," ","
...@@ -566,7 +570,11 @@ CREATE SCHEMA TEST_SCHEMA AUTHORIZATION SA ...@@ -566,7 +570,11 @@ CREATE SCHEMA TEST_SCHEMA AUTHORIZATION SA
"Commands (DDL)","CREATE SEQUENCE"," "Commands (DDL)","CREATE SEQUENCE","
CREATE SEQUENCE [ IF NOT EXISTS ] newSequenceName [ START WITH long ] CREATE SEQUENCE [ IF NOT EXISTS ] newSequenceName [ START WITH long ]
[ INCREMENT BY long ] [ CACHE long ] [ INCREMENT BY long ]
[ MINVALUE long | NOMINVALUE | NO MINVALUE ]
[ MAXVALUE long | NOMAXVALUE | NO MAXVALUE ]
[ CYCLE long | NOCYCLE | NO CYCLE ]
[ CACHE long | NOCACHE | NO CACHE ]
"," ","
Creates a new sequence. Creates a new sequence.
The data type of a sequence is BIGINT. The data type of a sequence is BIGINT.
......
...@@ -28,6 +28,7 @@ Change Log ...@@ -28,6 +28,7 @@ Change Log
</li><li>Issue 518: java.sql.Connection.commit() freezes after LOB modification with EXCLUSIVE connection </li><li>Issue 518: java.sql.Connection.commit() freezes after LOB modification with EXCLUSIVE connection
</li><li>Issue 517: Create or replace view statement has no effect on the others already existing JDBC connection </li><li>Issue 517: Create or replace view statement has no effect on the others already existing JDBC connection
</li><li>Support 123L syntax as in Java; example: SELECT (2000000000L*2). </li><li>Support 123L syntax as in Java; example: SELECT (2000000000L*2).
</li><li>Issue 520: Add support for sequence max value, min value and cycle, patch by Daniel Gredler.
</li></ul> </li></ul>
<h2>Version 1.3.174 (2013-10-19)</h2> <h2>Version 1.3.174 (2013-10-19)</h2>
......
...@@ -189,7 +189,7 @@ public class Parser { ...@@ -189,7 +189,7 @@ public class Parser {
private String sqlCommand; private String sqlCommand;
/** cached array if chars from sqlCommand */ /** cached array if chars from sqlCommand */
private char[] sqlCommandChars; private char[] sqlCommandChars;
/** index into sqlCommand of previous token */ /** index into sqlCommand of previous token */
private int lastParseIndex; private int lastParseIndex;
/** index into sqlCommand of current token */ /** index into sqlCommand of current token */
private int parseIndex; private int parseIndex;
...@@ -4183,8 +4183,34 @@ public class Parser { ...@@ -4183,8 +4183,34 @@ public class Parser {
} else if (readIf("INCREMENT")) { } else if (readIf("INCREMENT")) {
readIf("BY"); readIf("BY");
command.setIncrement(readExpression()); command.setIncrement(readExpression());
} else if (readIf("MINVALUE")) {
command.setMinValue(readExpression());
} else if (readIf("NOMINVALUE")) {
command.setMinValue(null);
} else if (readIf("MAXVALUE")) {
command.setMaxValue(readExpression());
} else if (readIf("NOMAXVALUE")) {
command.setMaxValue(null);
} else if (readIf("CYCLE")) {
command.setCycle(true);
} else if (readIf("NOCYCLE")) {
command.setCycle(false);
} else if (readIf("NO")) {
if (readIf("MINVALUE")) {
command.setMinValue(null);
} else if (readIf("MAXVALUE")) {
command.setMaxValue(null);
} else if (readIf("CYCLE")) {
command.setCycle(false);
} else if (readIf("CACHE")) {
command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
} else {
break;
}
} else if (readIf("CACHE")) { } else if (readIf("CACHE")) {
command.setCacheSize(readExpression()); command.setCacheSize(readExpression());
} else if (readIf("NOCACHE")) {
command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
} else if (readIf("BELONGS_TO_TABLE")) { } else if (readIf("BELONGS_TO_TABLE")) {
command.setBelongsToTable(true); command.setBelongsToTable(true);
} else { } else {
...@@ -4524,13 +4550,44 @@ public class Parser { ...@@ -4524,13 +4550,44 @@ public class Parser {
Sequence sequence = getSchema().getSequence(sequenceName); Sequence sequence = getSchema().getSequence(sequenceName);
AlterSequence command = new AlterSequence(session, sequence.getSchema()); AlterSequence command = new AlterSequence(session, sequence.getSchema());
command.setSequence(sequence); command.setSequence(sequence);
if (readIf("RESTART")) { while (true) {
read("WITH"); if (readIf("RESTART")) {
command.setStartWith(readExpression()); read("WITH");
} command.setStartWith(readExpression());
if (readIf("INCREMENT")) { } else if (readIf("INCREMENT")) {
read("BY"); read("BY");
command.setIncrement(readExpression()); command.setIncrement(readExpression());
} else if (readIf("MINVALUE")) {
command.setMinValue(readExpression());
} else if (readIf("NOMINVALUE")) {
command.setMinValue(null);
} else if (readIf("MAXVALUE")) {
command.setMaxValue(readExpression());
} else if (readIf("NOMAXVALUE")) {
command.setMaxValue(null);
} else if (readIf("CYCLE")) {
command.setCycle(true);
} else if (readIf("NOCYCLE")) {
command.setCycle(false);
} else if (readIf("NO")) {
if (readIf("MINVALUE")) {
command.setMinValue(null);
} else if (readIf("MAXVALUE")) {
command.setMaxValue(null);
} else if (readIf("CYCLE")) {
command.setCycle(false);
} else if (readIf("CACHE")) {
command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
} else {
break;
}
} else if (readIf("CACHE")) {
command.setCacheSize(readExpression());
} else if (readIf("NOCACHE")) {
command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
} else {
break;
}
} }
return command; return command;
} }
......
...@@ -23,6 +23,9 @@ public class CreateSequence extends SchemaCommand { ...@@ -23,6 +23,9 @@ public class CreateSequence extends SchemaCommand {
private String sequenceName; private String sequenceName;
private boolean ifNotExists; private boolean ifNotExists;
private boolean cycle;
private Expression minValue;
private Expression maxValue;
private Expression start; private Expression start;
private Expression increment; private Expression increment;
private Expression cacheSize; private Expression cacheSize;
...@@ -40,6 +43,10 @@ public class CreateSequence extends SchemaCommand { ...@@ -40,6 +43,10 @@ public class CreateSequence extends SchemaCommand {
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
} }
public void setCycle(boolean cycle) {
this.cycle = cycle;
}
@Override @Override
public int update() { public int update() {
session.commit(true); session.commit(true);
...@@ -51,17 +58,20 @@ public class CreateSequence extends SchemaCommand { ...@@ -51,17 +58,20 @@ public class CreateSequence extends SchemaCommand {
throw DbException.get(ErrorCode.SEQUENCE_ALREADY_EXISTS_1, sequenceName); throw DbException.get(ErrorCode.SEQUENCE_ALREADY_EXISTS_1, sequenceName);
} }
int id = getObjectId(); int id = getObjectId();
Sequence sequence = new Sequence(getSchema(), id, sequenceName, belongsToTable); Long startValue = getLong(start);
sequence.setStartValue(getLong(start, 1)); Long inc = getLong(increment);
sequence.setIncrement(getLong(increment, 1)); Long cache = getLong(cacheSize);
sequence.setCacheSize(getLong(cacheSize, Sequence.DEFAULT_CACHE_SIZE)); Long min = getLong(minValue);
Long max = getLong(maxValue);
Sequence sequence = new Sequence(getSchema(), id, sequenceName, startValue, inc,
cache, min, max, cycle, belongsToTable);
db.addSchemaObject(session, sequence); db.addSchemaObject(session, sequence);
return 0; return 0;
} }
private long getLong(Expression expr, long defaultValue) { private Long getLong(Expression expr) {
if (expr == null) { if (expr == null) {
return defaultValue; return null;
} }
return expr.optimize(session).getValue(session).getLong(); return expr.optimize(session).getValue(session).getLong();
} }
...@@ -74,6 +84,14 @@ public class CreateSequence extends SchemaCommand { ...@@ -74,6 +84,14 @@ public class CreateSequence extends SchemaCommand {
this.increment = increment; this.increment = increment;
} }
public void setMinValue(Expression minValue) {
this.minValue = minValue;
}
public void setMaxValue(Expression maxValue) {
this.maxValue = maxValue;
}
public void setBelongsToTable(boolean belongsToTable) { public void setBelongsToTable(boolean belongsToTable) {
this.belongsToTable = belongsToTable; this.belongsToTable = belongsToTable;
} }
......
...@@ -29,6 +29,10 @@ public class AlterSequence extends SchemaCommand { ...@@ -29,6 +29,10 @@ public class AlterSequence extends SchemaCommand {
private Sequence sequence; private Sequence sequence;
private Expression start; private Expression start;
private Expression increment; private Expression increment;
private Boolean cycle;
private Expression minValue;
private Expression maxValue;
private Expression cacheSize;
public AlterSequence(Session session, Schema schema) { public AlterSequence(Session session, Schema schema) {
super(session, schema); super(session, schema);
...@@ -59,22 +63,41 @@ public class AlterSequence extends SchemaCommand { ...@@ -59,22 +63,41 @@ public class AlterSequence extends SchemaCommand {
this.increment = increment; this.increment = increment;
} }
public void setCycle(Boolean cycle) {
this.cycle = cycle;
}
public void setMinValue(Expression minValue) {
this.minValue = minValue;
}
public void setMaxValue(Expression maxValue) {
this.maxValue = maxValue;
}
public void setCacheSize(Expression cacheSize) {
this.cacheSize = cacheSize;
}
@Override @Override
public int update() { public int update() {
Database db = session.getDatabase(); Database db = session.getDatabase();
if (table != null) { if (table != null) {
session.getUser().checkRight(table, Right.ALL); session.getUser().checkRight(table, Right.ALL);
} }
if (start != null) { if (cycle != null) {
long startValue = start.optimize(session).getValue(session).getLong(); sequence.setCycle(cycle);
sequence.setStartValue(startValue);
} }
if (increment != null) { if (cacheSize != null) {
long incrementValue = increment.optimize(session).getValue(session).getLong(); long size = cacheSize.optimize(session).getValue(session).getLong();
if (incrementValue == 0) { sequence.setCacheSize(size);
throw DbException.getInvalidValueException("INCREMENT", 0); }
} if (start != null || minValue != null || maxValue != null || increment != null) {
sequence.setIncrement(incrementValue); Long startValue = getLong(start);
Long min = getLong(minValue);
Long max = getLong(maxValue);
Long inc = getLong(increment);
sequence.modify(startValue, min, max, inc);
} }
// need to use the system session, so that the update // need to use the system session, so that the update
// can be committed immediately - not committing it // can be committed immediately - not committing it
...@@ -87,6 +110,13 @@ public class AlterSequence extends SchemaCommand { ...@@ -87,6 +110,13 @@ public class AlterSequence extends SchemaCommand {
return 0; return 0;
} }
private Long getLong(Expression expr) {
if (expr == null) {
return null;
}
return expr.optimize(session).getValue(session).getLong();
}
@Override @Override
public int getType() { public int getType() {
return CommandInterface.ALTER_SEQUENCE; return CommandInterface.ALTER_SEQUENCE;
......
...@@ -458,6 +458,13 @@ public class ErrorCode { ...@@ -458,6 +458,13 @@ public class ErrorCode {
*/ */
public static final int TRIGGER_SELECT_AND_ROW_BASED_NOT_SUPPORTED = 90005; public static final int TRIGGER_SELECT_AND_ROW_BASED_NOT_SUPPORTED = 90005;
/**
* The error with code <code>90006</code> is thrown when
* trying to get a value from a sequence that has run out of numbers
* and does not have cycling enabled.
*/
public static final int SEQUENCE_EXHAUSTED = 90006;
/** /**
* The error with code <code>90007</code> is thrown when * The error with code <code>90007</code> is thrown when
* trying to call a JDBC method on an object that has been closed. * trying to call a JDBC method on an object that has been closed.
...@@ -474,6 +481,13 @@ public class ErrorCode { ...@@ -474,6 +481,13 @@ public class ErrorCode {
*/ */
public static final int INVALID_VALUE_2 = 90008; public static final int INVALID_VALUE_2 = 90008;
/**
* The error with code <code>90009</code> is thrown when
* trying to create a sequence with an invalid combination
* of attributes (min value, max value, start value, etc).
*/
public static final int SEQUENCE_ATTRIBUTES_INVALID = 90009;
/** /**
* The error with code <code>22007</code> is thrown when * The error with code <code>22007</code> is thrown when
* a text can not be converted to a date, time, or timestamp constant. * a text can not be converted to a date, time, or timestamp constant.
...@@ -1870,7 +1884,7 @@ public class ErrorCode { ...@@ -1870,7 +1884,7 @@ public class ErrorCode {
public static final int JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE = 90141; public static final int JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE = 90141;
// next are 90006, 90009, 90010, 90011, 90021, 90039, // next are 90010, 90011, 90021, 90039,
// 90051, 90056, 90110, 90122, 90142 // 90051, 90056, 90110, 90122, 90142
private ErrorCode() { private ErrorCode() {
......
...@@ -33,8 +33,10 @@ ...@@ -33,8 +33,10 @@
90002=Method is only allowed for a query. Use execute or executeUpdate instead of executeQuery 90002=Method is only allowed for a query. Use execute or executeUpdate instead of executeQuery
90003=Hexadecimal string with odd number of characters: {0} 90003=Hexadecimal string with odd number of characters: {0}
90004=Hexadecimal string contains non-hex character: {0} 90004=Hexadecimal string contains non-hex character: {0}
90006=Sequence {0} has run out of numbers
90007=The object is already closed 90007=The object is already closed
90008=Invalid value {0} for parameter {1} 90008=Invalid value {0} for parameter {1}
90009=Unable to create or alter sequence {0} because of invalid attributes (start value {1}, min value {2}, max value {3}, increment {4})
90012=Parameter {0} is not set 90012=Parameter {0} is not set
90013=Database {0} not found 90013=Database {0} not found
90014=Error parsing {0} 90014=Error parsing {0}
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
*/ */
package org.h2.schema; package org.h2.schema;
import java.math.BigInteger;
import org.h2.constant.ErrorCode;
import org.h2.engine.DbObject; import org.h2.engine.DbObject;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -13,7 +15,7 @@ import org.h2.message.Trace; ...@@ -13,7 +15,7 @@ import org.h2.message.Trace;
import org.h2.table.Table; import org.h2.table.Table;
/** /**
*A sequence is created using the statement * A sequence is created using the statement
* CREATE SEQUENCE * CREATE SEQUENCE
*/ */
public class Sequence extends SchemaObjectBase { public class Sequence extends SchemaObjectBase {
...@@ -23,20 +25,109 @@ public class Sequence extends SchemaObjectBase { ...@@ -23,20 +25,109 @@ public class Sequence extends SchemaObjectBase {
*/ */
public static final int DEFAULT_CACHE_SIZE = 32; public static final int DEFAULT_CACHE_SIZE = 32;
private long value = 1; private long value;
private long valueWithMargin; private long valueWithMargin;
private long increment = 1; private long increment;
private long cacheSize = DEFAULT_CACHE_SIZE; private long cacheSize;
private long minValue;
private long maxValue;
private boolean cycle;
private boolean belongsToTable; private boolean belongsToTable;
public Sequence(Schema schema, int id, String name, boolean belongsToTable) { /**
* Creates a new sequence for an auto-increment column.
*/
public Sequence(Schema schema, int id, String name, long startValue, long increment) {
this(schema, id, name, startValue, increment, null, null, null, false, true);
}
/**
* Creates a new sequence.
*/
public Sequence(Schema schema, int id, String name, Long startValue, Long increment, Long cacheSize,
Long minValue, Long maxValue, boolean cycle, boolean belongsToTable) {
initSchemaObjectBase(schema, id, name, Trace.SEQUENCE); initSchemaObjectBase(schema, id, name, Trace.SEQUENCE);
this.increment = increment != null ? increment : 1;
this.minValue = minValue != null ? minValue : getDefaultMinValue(this.increment);
this.maxValue = maxValue != null ? maxValue : getDefaultMaxValue(this.increment);
this.value = startValue != null ? startValue : getDefaultStartValue(this.increment);
this.valueWithMargin = value;
this.cacheSize = cacheSize != null ? Math.max(1, cacheSize) : DEFAULT_CACHE_SIZE;
this.cycle = cycle;
this.belongsToTable = belongsToTable; this.belongsToTable = belongsToTable;
if (!isValid(this.value, this.minValue, this.maxValue, this.increment)) {
throw DbException.get(ErrorCode.SEQUENCE_ATTRIBUTES_INVALID, name, String.valueOf(this.value),
String.valueOf(this.minValue), String.valueOf(this.maxValue), String.valueOf(this.increment));
}
} }
public synchronized void setStartValue(long value) { /**
this.value = value; * Allows the start value, increment, min value and max value to be updated
this.valueWithMargin = value; * atomically, including atomic validation. Useful because setting these
* attributes one after the other could otherwise result in an invalid
* sequence state (e.g. min value > max value, start value < min value,
* etc).
*
* @param startValue the new start value (<code>null</code> if no change)
* @param minValue the new min value (<code>null</code> if no change)
* @param maxValue the new max value (<code>null</code> if no change)
* @param increment the new increment (<code>null</code> if no change)
*/
public synchronized void modify(Long startValue, Long minValue, Long maxValue, Long increment) {
if (startValue == null) {
startValue = this.value;
}
if (minValue == null) {
minValue = this.minValue;
}
if (maxValue == null) {
maxValue = this.maxValue;
}
if (increment == null) {
increment = this.increment;
}
if (!isValid(startValue, minValue, maxValue, increment)) {
throw DbException.get(ErrorCode.SEQUENCE_ATTRIBUTES_INVALID, getName(), String.valueOf(this.value),
String.valueOf(this.minValue), String.valueOf(this.maxValue), String.valueOf(this.increment));
}
this.value = startValue;
this.valueWithMargin = startValue;
this.minValue = minValue;
this.maxValue = maxValue;
this.increment = increment;
}
/**
* Validates the specified prospective start value, min value, max value and increment relative
* to each other, since each of their respective validities are contingent on the values of the
* other parameters.
*
* @param value the prospective start value
* @param minValue the prospective min value
* @param maxValue the prospective max value
* @param increment the prospective increment
*/
private static boolean isValid(long value, long minValue, long maxValue, long increment) {
return minValue <= value &&
maxValue >= value &&
maxValue > minValue &&
increment != 0 &&
// Math.abs(increment) < maxValue - minValue
// use BigInteger to avoid overflows when maxValue and minValue are really big
BigInteger.valueOf(increment).abs().compareTo(
BigInteger.valueOf(maxValue).subtract(BigInteger.valueOf(minValue))) < 0;
}
private long getDefaultMinValue(long increment) {
return increment >= 0 ? 1 : Long.MIN_VALUE;
}
private long getDefaultMaxValue(long increment) {
return increment >= 0 ? Long.MAX_VALUE : -1;
}
private long getDefaultStartValue(long increment) {
return increment >= 0 ? minValue : maxValue;
} }
public boolean getBelongsToTable() { public boolean getBelongsToTable() {
...@@ -47,11 +138,20 @@ public class Sequence extends SchemaObjectBase { ...@@ -47,11 +138,20 @@ public class Sequence extends SchemaObjectBase {
return increment; return increment;
} }
public void setIncrement(long inc) { public long getMinValue() {
if (inc == 0) { return minValue;
throw DbException.getInvalidValueException("INCREMENT", 0); }
}
this.increment = inc; public long getMaxValue() {
return maxValue;
}
public boolean getCycle() {
return cycle;
}
public void setCycle(boolean cycle) {
this.cycle = cycle;
} }
@Override @Override
...@@ -74,6 +174,15 @@ public class Sequence extends SchemaObjectBase { ...@@ -74,6 +174,15 @@ public class Sequence extends SchemaObjectBase {
if (increment != 1) { if (increment != 1) {
buff.append(" INCREMENT BY ").append(increment); buff.append(" INCREMENT BY ").append(increment);
} }
if (minValue != getDefaultMinValue(increment)) {
buff.append(" MINVALUE ").append(minValue);
}
if (maxValue != getDefaultMaxValue(increment)) {
buff.append(" MAXVALUE ").append(maxValue);
}
if (cycle) {
buff.append(" CYCLE");
}
if (cacheSize != DEFAULT_CACHE_SIZE) { if (cacheSize != DEFAULT_CACHE_SIZE) {
buff.append(" CACHE ").append(cacheSize); buff.append(" CACHE ").append(cacheSize);
} }
...@@ -90,8 +199,21 @@ public class Sequence extends SchemaObjectBase { ...@@ -90,8 +199,21 @@ public class Sequence extends SchemaObjectBase {
* @return the next value * @return the next value
*/ */
public synchronized long getNext(Session session) { public synchronized long getNext(Session session) {
boolean needsFlush = false;
if ((increment > 0 && value >= valueWithMargin) || (increment < 0 && value <= valueWithMargin)) { if ((increment > 0 && value >= valueWithMargin) || (increment < 0 && value <= valueWithMargin)) {
valueWithMargin += increment * cacheSize; valueWithMargin += increment * cacheSize;
needsFlush = true;
}
if ((increment > 0 && value > maxValue) || (increment < 0 && value < minValue)) {
if (cycle) {
value = increment > 0 ? minValue : maxValue;
valueWithMargin = value + (increment * cacheSize);
needsFlush = true;
} else {
throw DbException.get(ErrorCode.SEQUENCE_EXHAUSTED, getName());
}
}
if (needsFlush) {
flush(session); flush(session);
} }
long v = value; long v = value;
......
...@@ -334,7 +334,7 @@ public class Column { ...@@ -334,7 +334,7 @@ public class Column {
update = true; update = true;
} }
if (update) { if (update) {
sequence.setStartValue(now + inc); sequence.modify(now + inc, null, null, null);
session.setLastIdentity(ValueLong.get(now)); session.setLastIdentity(ValueLong.get(now));
sequence.flush(session); sequence.flush(session);
} }
...@@ -370,9 +370,7 @@ public class Column { ...@@ -370,9 +370,7 @@ public class Column {
break; break;
} }
} }
Sequence seq = new Sequence(schema, id, sequenceName, true); Sequence seq = new Sequence(schema, id, sequenceName, start, increment);
seq.setStartValue(start);
seq.setIncrement(increment);
if (!temporary) { if (!temporary) {
session.getDatabase().addSchemaObject(session, seq); session.getDatabase().addSchemaObject(session, seq);
} }
......
...@@ -252,6 +252,9 @@ public class MetaTable extends Table { ...@@ -252,6 +252,9 @@ public class MetaTable extends Table {
"IS_GENERATED BIT", "IS_GENERATED BIT",
"REMARKS", "REMARKS",
"CACHE BIGINT", "CACHE BIGINT",
"MIN_VALUE BIGINT",
"MAX_VALUE BIGINT",
"IS_CYCLE BIT",
"ID INT" "ID INT"
); );
break; break;
...@@ -1007,10 +1010,16 @@ public class MetaTable extends Table { ...@@ -1007,10 +1010,16 @@ public class MetaTable extends Table {
String.valueOf(s.getIncrement()), String.valueOf(s.getIncrement()),
// IS_GENERATED // IS_GENERATED
s.getBelongsToTable() ? "TRUE" : "FALSE", s.getBelongsToTable() ? "TRUE" : "FALSE",
// REMARKS // REMARKS
replaceNullWithEmpty(s.getComment()), replaceNullWithEmpty(s.getComment()),
// CACHE // CACHE
String.valueOf(s.getCacheSize()), String.valueOf(s.getCacheSize()),
// MIN_VALUE
String.valueOf(s.getMinValue()),
// MAX_VALUE
String.valueOf(s.getMaxValue()),
// IS_CYCLE
s.getCycle() ? "TRUE" : "FALSE",
// ID // ID
"" + s.getId() "" + s.getId()
); );
......
...@@ -10,7 +10,9 @@ import java.sql.Connection; ...@@ -10,7 +10,9 @@ import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.h2.test.TestBase; import org.h2.test.TestBase;
/** /**
...@@ -34,6 +36,11 @@ public class TestSequence extends TestBase { ...@@ -34,6 +36,11 @@ public class TestSequence extends TestBase {
testAlterSequence(); testAlterSequence();
testCache(); testCache();
testTwo(); testTwo();
testMetaTable();
testCreateWithMinValue();
testCreateWithMaxValue();
testCreationErrors();
testCreateSql();
deleteDb("sequence"); deleteDb("sequence");
} }
...@@ -62,16 +69,12 @@ public class TestSequence extends TestBase { ...@@ -62,16 +69,12 @@ public class TestSequence extends TestBase {
} }
private void testAlterSequence() throws SQLException { private void testAlterSequence() throws SQLException {
deleteDb("sequence"); test("create sequence s; alter sequence s restart with 2", null, 2, 3, 4);
Connection conn = getConnection("sequence"); test("create sequence s; alter sequence s restart with 7", null, 7, 8, 9, 10);
Statement stat = conn.createStatement(); test("create sequence s; alter sequence s restart with 11 minvalue 3 maxvalue 12 cycle", null, 11, 12, 3, 4);
stat.execute("create sequence test"); test("create sequence s; alter sequence s restart with 5 cache 2", null, 5, 6, 7, 8);
conn.setAutoCommit(false); test("create sequence s; alter sequence s restart with 9 maxvalue 12 nocycle nocache",
stat.execute("alter sequence test restart with 1"); "Sequence \"S\" has run out of numbers", 9, 10, 11, 12);
for (int i = 0; i < 40; i++) {
stat.execute("select nextval('test')");
}
conn.close();
} }
private void testCache() throws SQLException { private void testCache() throws SQLException {
...@@ -99,11 +102,142 @@ public class TestSequence extends TestBase { ...@@ -99,11 +102,142 @@ public class TestSequence extends TestBase {
conn.close(); conn.close();
} }
private void testMetaTable() throws SQLException {
deleteDb("sequence");
Connection conn = getConnection("sequence");
Statement stat = conn.createStatement();
stat.execute("create sequence a");
stat.execute("create sequence b start with 7 minvalue 5 maxvalue 9 cycle increment by 2 nocache");
stat.execute("create sequence c start with -4 minvalue -9 maxvalue -3 no cycle increment by -2 cache 3");
conn.close();
conn = getConnection("sequence");
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select * from information_schema.sequences order by sequence_name");
rs.next();
assertEquals("SEQUENCE", rs.getString("SEQUENCE_CATALOG"));
assertEquals("PUBLIC", rs.getString("SEQUENCE_SCHEMA"));
assertEquals("A", rs.getString("SEQUENCE_NAME"));
assertEquals(0, rs.getLong("CURRENT_VALUE"));
assertEquals(1, rs.getLong("INCREMENT"));
assertEquals(false, rs.getBoolean("IS_GENERATED"));
assertEquals("", rs.getString("REMARKS"));
assertEquals(32, rs.getLong("CACHE"));
assertEquals(1, rs.getLong("MIN_VALUE"));
assertEquals(Long.MAX_VALUE, rs.getLong("MAX_VALUE"));
assertEquals(false, rs.getBoolean("IS_CYCLE"));
rs.next();
assertEquals("SEQUENCE", rs.getString("SEQUENCE_CATALOG"));
assertEquals("PUBLIC", rs.getString("SEQUENCE_SCHEMA"));
assertEquals("B", rs.getString("SEQUENCE_NAME"));
assertEquals(5, rs.getLong("CURRENT_VALUE"));
assertEquals(2, rs.getLong("INCREMENT"));
assertEquals(false, rs.getBoolean("IS_GENERATED"));
assertEquals("", rs.getString("REMARKS"));
assertEquals(1, rs.getLong("CACHE"));
assertEquals(5, rs.getLong("MIN_VALUE"));
assertEquals(9, rs.getLong("MAX_VALUE"));
assertEquals(true, rs.getBoolean("IS_CYCLE"));
rs.next();
assertEquals("SEQUENCE", rs.getString("SEQUENCE_CATALOG"));
assertEquals("PUBLIC", rs.getString("SEQUENCE_SCHEMA"));
assertEquals("C", rs.getString("SEQUENCE_NAME"));
assertEquals(-2, rs.getLong("CURRENT_VALUE"));
assertEquals(-2, rs.getLong("INCREMENT"));
assertEquals(false, rs.getBoolean("IS_GENERATED"));
assertEquals("", rs.getString("REMARKS"));
assertEquals(3, rs.getLong("CACHE"));
assertEquals(-9, rs.getLong("MIN_VALUE"));
assertEquals(-3, rs.getLong("MAX_VALUE"));
assertEquals(false, rs.getBoolean("IS_CYCLE"));
assertFalse(rs.next());
conn.close();
}
private void testCreateWithMinValue() throws SQLException {
test("create sequence s minvalue 3", null, 3, 4, 5, 6);
test("create sequence s minvalue -3 increment by -1 cycle", null, -1, -2, -3, -1);
test("create sequence s minvalue -3 increment by -1", "Sequence \"S\" has run out of numbers", -1, -2, -3);
test("create sequence s minvalue -3 increment by -1 nocycle", "Sequence \"S\" has run out of numbers", -1, -2, -3);
test("create sequence s minvalue -3 increment by -1 no cycle", "Sequence \"S\" has run out of numbers", -1, -2, -3);
test("create sequence s minvalue -3 increment by -1 nocache cycle", null, -1, -2, -3, -1);
test("create sequence s minvalue -3 increment by -1 nocache", "Sequence \"S\" has run out of numbers", -1, -2, -3);
test("create sequence s minvalue -3 increment by -1 nocache nocycle", "Sequence \"S\" has run out of numbers", -1, -2, -3);
test("create sequence s minvalue -3 increment by -1 no cache no cycle",
"Sequence \"S\" has run out of numbers", -1, -2, -3);
}
private void testCreateWithMaxValue() throws SQLException {
test("create sequence s maxvalue -3 increment by -1", null, -3, -4, -5, -6);
test("create sequence s maxvalue 3 cycle", null, 1, 2, 3, 1);
test("create sequence s maxvalue 3", "Sequence \"S\" has run out of numbers", 1, 2, 3);
test("create sequence s maxvalue 3 nocycle", "Sequence \"S\" has run out of numbers", 1, 2, 3);
test("create sequence s maxvalue 3 no cycle", "Sequence \"S\" has run out of numbers", 1, 2, 3);
test("create sequence s maxvalue 3 nocache cycle", null, 1, 2, 3, 1);
test("create sequence s maxvalue 3 nocache", "Sequence \"S\" has run out of numbers", 1, 2, 3);
test("create sequence s maxvalue 3 nocache nocycle", "Sequence \"S\" has run out of numbers", 1, 2, 3);
test("create sequence s maxvalue 3 no cache no cycle", "Sequence \"S\" has run out of numbers", 1, 2, 3);
}
private void testCreationErrors() throws SQLException {
deleteDb("sequence");
Connection conn = getConnection("sequence");
Statement stat = conn.createStatement();
expectError(
stat,
"create sequence a minvalue 5 start with 2",
"Unable to create or alter sequence \"A\" because of invalid attributes (start value \"2\", "
+ "min value \"5\", max value \"" + Long.MAX_VALUE + "\", increment \"1\")");
expectError(
stat,
"create sequence b maxvalue 5 start with 7",
"Unable to create or alter sequence \"B\" because of invalid attributes (start value \"7\", "
+ "min value \"1\", max value \"5\", increment \"1\")");
expectError(
stat,
"create sequence c minvalue 5 maxvalue 2",
"Unable to create or alter sequence \"C\" because of invalid attributes (start value \"5\", "
+ "min value \"5\", max value \"2\", increment \"1\")");
expectError(
stat,
"create sequence d increment by 0",
"Unable to create or alter sequence \"D\" because of invalid attributes (start value \"1\", "
+ "min value \"1\", max value \"" + Long.MAX_VALUE + "\", increment \"0\")");
expectError(
stat,
"create sequence e minvalue 1 maxvalue 5 increment 99",
"Unable to create or alter sequence \"E\" because of invalid attributes (start value \"1\", "
+ "min value \"1\", max value \"5\", increment \"99\")");
conn.close();
}
private void testCreateSql() throws SQLException {
deleteDb("sequence");
Connection conn = getConnection("sequence");
Statement stat = conn.createStatement();
stat.execute("create sequence a");
stat.execute("create sequence b start with 5 increment by 2 minvalue 3 maxvalue 7 cycle nocache");
stat.execute("create sequence c start with 3 increment by 1 minvalue 2 maxvalue 9 nocycle cache 2");
stat.execute("create sequence d nomaxvalue no minvalue no cache nocycle");
stat.execute("create sequence e cache 1");
List<String> script = new ArrayList<String>();
ResultSet rs = stat.executeQuery("script nodata");
while (rs.next()) {
script.add(rs.getString(1));
}
Collections.sort(script);
assertEquals("CREATE SEQUENCE PUBLIC.A START WITH 1;", script.get(0));
assertEquals("CREATE SEQUENCE PUBLIC.B START WITH 5 INCREMENT BY 2 MINVALUE 3 MAXVALUE 7 CYCLE CACHE 1;", script.get(1));
assertEquals("CREATE SEQUENCE PUBLIC.C START WITH 3 MINVALUE 2 MAXVALUE 9 CACHE 2;", script.get(2));
assertEquals("CREATE SEQUENCE PUBLIC.D START WITH 1 CACHE 1;", script.get(3));
assertEquals("CREATE SEQUENCE PUBLIC.E START WITH 1 CACHE 1;", script.get(4));
conn.close();
}
private void testTwo() throws SQLException { private void testTwo() throws SQLException {
deleteDb("sequence"); deleteDb("sequence");
Connection conn = getConnection("sequence"); Connection conn = getConnection("sequence");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("create sequence testSequence"); stat.execute("create sequence s");
conn.setAutoCommit(false); conn.setAutoCommit(false);
Connection conn2 = getConnection("sequence"); Connection conn2 = getConnection("sequence");
...@@ -126,8 +260,44 @@ public class TestSequence extends TestBase { ...@@ -126,8 +260,44 @@ public class TestSequence extends TestBase {
conn.close(); conn.close();
} }
private void test(String setupSql, String finalError, long... values) throws SQLException {
deleteDb("sequence");
Connection conn = getConnection("sequence");
Statement stat = conn.createStatement();
stat.execute(setupSql);
conn.close();
conn = getConnection("sequence");
stat = conn.createStatement();
for (long value : values) {
assertEquals(value, getNext(stat));
}
if (finalError != null) {
try {
getNext(stat);
fail("Expected error: " + finalError);
} catch (SQLException e) {
assertTrue(e.getMessage().contains(finalError));
}
}
conn.close();
}
private void expectError(Statement stat, String sql, String error) {
try {
stat.execute(sql);
fail("Expected error: " + error);
} catch (SQLException e) {
assertTrue(e.getMessage(), e.getMessage().contains(error));
}
}
private static long getNext(Statement stat) throws SQLException { private static long getNext(Statement stat) throws SQLException {
ResultSet rs = stat.executeQuery("call next value for testSequence"); ResultSet rs = stat.executeQuery("call next value for s");
rs.next(); rs.next();
long value = rs.getLong(1); long value = rs.getLong(1);
return value; return value;
......
...@@ -8319,7 +8319,7 @@ DROP SEQUENCE IF EXISTS TEST_SEQ; ...@@ -8319,7 +8319,7 @@ DROP SEQUENCE IF EXISTS TEST_SEQ;
DROP SEQUENCE IF EXISTS TEST_SEQ; DROP SEQUENCE IF EXISTS TEST_SEQ;
> ok > ok
CREATE SEQUENCE TEST_LONG START WITH 90123456789012345 INCREMENT BY -1; CREATE SEQUENCE TEST_LONG START WITH 90123456789012345 MAXVALUE 90123456789012345 INCREMENT BY -1;
> ok > ok
SET AUTOCOMMIT FALSE; SET AUTOCOMMIT FALSE;
......
...@@ -740,4 +740,6 @@ persists forwarding periods discussion whatever revisit decision ...@@ -740,4 +740,6 @@ persists forwarding periods discussion whatever revisit decision
detrimental dedicated kaiser perhaps chromium shortened detrimental dedicated kaiser perhaps chromium shortened
layers waited descent spliced abstracts planning interest among sliced layers waited descent spliced abstracts planning interest among sliced
lives pauses allocates kicks introduction straightforward getenv lives pauses allocates kicks introduction straightforward getenv
ordinate tweaking fetching rfe yates cookie btrfs cookies ordinate tweaking fetching rfe yates cookie btrfs cookies
\ No newline at end of file nocycle nomaxvalue nominvalue cycling proceed prospective exhausted contingent
validities hang degenerates freezes
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论