Unverified 提交 1b8bb91c authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1551 from katzyn/for_update

Add support for SELECT FOR UPDATE with JOINs in MVStore mode
...@@ -39,10 +39,10 @@ Multiple set operators (UNION, INTERSECT, MINUS, EXCEPT) are evaluated ...@@ -39,10 +39,10 @@ Multiple set operators (UNION, INTERSECT, MINUS, EXCEPT) are evaluated
from left to right. For compatibility with other databases and future versions from left to right. For compatibility with other databases and future versions
of H2 please use parentheses. of H2 please use parentheses.
If FOR UPDATE is specified, the tables are locked for writing. When using If FOR UPDATE is specified, the tables or rows are locked for writing.
MVCC, only the selected rows are locked as in an UPDATE statement. This clause is not allowed in DISTINCT queries and in queries with non-window aggregates, GROUP BY, or HAVING clauses.
In this case, aggregate, GROUP BY, DISTINCT queries or joins When using default MVStore engine only the selected rows are locked as in an UPDATE statement.
are not allowed in this case. With PageStore engine the whole tables are locked.
"," ","
SELECT * FROM TEST; SELECT * FROM TEST;
SELECT * FROM TEST ORDER BY NAME; SELECT * FROM TEST ORDER BY NAME;
......
...@@ -21,6 +21,14 @@ Change Log ...@@ -21,6 +21,14 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #1549: [RFE] Implement locking modes (select for update)
</li>
<li>PR #1548: Add AsynchronousFileChannel-based experimental FilePathAsync
</li>
<li>PR #1547: Speedup unused chunks collection
</li>
<li>PR #1546: Tiny optimization: use `System.arraycopy` when possible
</li>
<li>PR #1545: Export datetime value functions to SQL using standard syntax <li>PR #1545: Export datetime value functions to SQL using standard syntax
</li> </li>
<li>Issue #1371: NPE in CacheLRU <li>Issue #1371: NPE in CacheLRU
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=Step size must not be zero 90142=Step size must not be zero
90143=Row {1} not found in primary index {0} 90143=Row {1} not found in primary index {0}
90144=Authenticator not enabled on database {0} 90144=Authenticator not enabled on database {0}
90145=FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=General error: {0} HY000=General error: {0}
HY004=Unknown data type: {0} HY004=Unknown data type: {0}
HYC00=Feature not supported: {0} HYC00=Feature not supported: {0}
......
...@@ -2030,8 +2030,20 @@ public class ErrorCode { ...@@ -2030,8 +2030,20 @@ public class ErrorCode {
*/ */
public static final int AUTHENTICATOR_NOT_AVAILABLE = 90144; public static final int AUTHENTICATOR_NOT_AVAILABLE = 90144;
/**
* The error with code <code>90145</code> is thrown when trying to execute a
* SELECT statement with non-window aggregates, DISTINCT, GROUP BY, or
* HAVING clauses together with FOR UPDATE clause.
*
* <pre>
* SELECT DISTINCT NAME FOR UPDATE;
* SELECT MAX(VALUE) FOR UPDATE;
* </pre>
*/
public static final int FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT = 90145;
// next is 90145 // next is 90146
private ErrorCode() { private ErrorCode() {
// utility class // utility class
......
...@@ -442,10 +442,12 @@ public class Select extends Query { ...@@ -442,10 +442,12 @@ public class Select extends Query {
int rowNumber = 0; int rowNumber = 0;
setCurrentRowNumber(0); setCurrentRowNumber(0);
int sampleSize = getSampleSizeValue(session); int sampleSize = getSampleSizeValue(session);
ArrayList<Row>[] forUpdateRows = initForUpdateRows();
while (topTableFilter.next()) { while (topTableFilter.next()) {
setCurrentRowNumber(rowNumber + 1); setCurrentRowNumber(rowNumber + 1);
if (isConditionMet()) { if (isConditionMet()) {
rowNumber++; rowNumber++;
addForUpdateRow(forUpdateRows);
groupData.nextSource(); groupData.nextSource();
updateAgg(columnCount, stage); updateAgg(columnCount, stage);
if (sampleSize > 0 && rowNumber >= sampleSize) { if (sampleSize > 0 && rowNumber >= sampleSize) {
...@@ -453,6 +455,7 @@ public class Select extends Query { ...@@ -453,6 +455,7 @@ public class Select extends Query {
} }
} }
} }
lockForUpdateRows(forUpdateRows);
groupData.done(); groupData.done();
} }
...@@ -627,7 +630,7 @@ public class Select extends Query { ...@@ -627,7 +630,7 @@ public class Select extends Query {
limitRows = Long.MAX_VALUE; limitRows = Long.MAX_VALUE;
} }
} }
ArrayList<Row> forUpdateRows = this.isForUpdateMvcc ? Utils.<Row>newSmallArrayList() : null; ArrayList<Row>[] forUpdateRows = initForUpdateRows();
int sampleSize = getSampleSizeValue(session); int sampleSize = getSampleSizeValue(session);
LazyResultQueryFlat lazyResult = new LazyResultQueryFlat(expressionArray, LazyResultQueryFlat lazyResult = new LazyResultQueryFlat(expressionArray,
sampleSize, columnCount); sampleSize, columnCount);
...@@ -640,9 +643,7 @@ public class Select extends Query { ...@@ -640,9 +643,7 @@ public class Select extends Query {
} }
Value[] row = null; Value[] row = null;
while (result.getRowCount() < limitRows && lazyResult.next()) { while (result.getRowCount() < limitRows && lazyResult.next()) {
if (forUpdateRows != null) { addForUpdateRow(forUpdateRows);
topTableFilter.lockRowAdd(forUpdateRows);
}
row = lazyResult.currentRow(); row = lazyResult.currentRow();
result.addRow(row); result.addRow(row);
} }
...@@ -653,18 +654,45 @@ public class Select extends Query { ...@@ -653,18 +654,45 @@ public class Select extends Query {
if (sort.compare(expected, row) != 0) { if (sort.compare(expected, row) != 0) {
break; break;
} }
if (forUpdateRows != null) { addForUpdateRow(forUpdateRows);
topTableFilter.lockRowAdd(forUpdateRows);
}
result.addRow(row); result.addRow(row);
} }
result.limitsWereApplied(); result.limitsWereApplied();
} }
if (forUpdateRows != null) { lockForUpdateRows(forUpdateRows);
topTableFilter.lockRows(forUpdateRows); return null;
} }
private ArrayList<Row>[] initForUpdateRows() {
if (!this.isForUpdateMvcc) {
return null; return null;
} }
int count = filters.size();
@SuppressWarnings("unchecked")
ArrayList<Row>[] rows = new ArrayList[count];
for (int i = 0; i < count; i++) {
rows[i] = Utils.<Row>newSmallArrayList();
}
return rows;
}
private void addForUpdateRow(ArrayList<Row>[] forUpdateRows) {
if (forUpdateRows != null) {
int count = filters.size();
for (int i = 0; i < count; i++) {
filters.get(i).lockRowAdd(forUpdateRows[i]);
}
}
}
private void lockForUpdateRows(ArrayList<Row>[] forUpdateRows) {
if (forUpdateRows != null) {
int count = filters.size();
for (int i = 0; i < count; i++) {
filters.get(i).lockRows(forUpdateRows[i]);
}
}
}
private static void skipOffset(LazyResultSelect lazyResult, long offset, boolean quickOffset) { private static void skipOffset(LazyResultSelect lazyResult, long offset, boolean quickOffset) {
if (quickOffset) { if (quickOffset) {
...@@ -763,21 +791,6 @@ public class Select extends Query { ...@@ -763,21 +791,6 @@ public class Select extends Query {
topTableFilter.startQuery(session); topTableFilter.startQuery(session);
topTableFilter.reset(); topTableFilter.reset();
boolean exclusive = isForUpdate && !isForUpdateMvcc; boolean exclusive = isForUpdate && !isForUpdateMvcc;
if (isForUpdateMvcc) {
if (isGroupQuery) {
throw DbException.getUnsupportedException(
"MVCC=TRUE && FOR UPDATE && GROUP");
} else if (isAnyDistinct()) {
throw DbException.getUnsupportedException(
"MVCC=TRUE && FOR UPDATE && DISTINCT");
} else if (isQuickAggregateQuery) {
throw DbException.getUnsupportedException(
"MVCC=TRUE && FOR UPDATE && AGGREGATE");
} else if (topTableFilter.getJoin() != null) {
throw DbException.getUnsupportedException(
"MVCC=TRUE && FOR UPDATE && JOIN");
}
}
topTableFilter.lock(session, exclusive, exclusive); topTableFilter.lock(session, exclusive, exclusive);
ResultTarget to = result != null ? result : target; ResultTarget to = result != null ? result : target;
lazy &= to == null; lazy &= to == null;
...@@ -1451,9 +1464,11 @@ public class Select extends Query { ...@@ -1451,9 +1464,11 @@ public class Select extends Query {
@Override @Override
public void setForUpdate(boolean b) { public void setForUpdate(boolean b) {
if (b && (isAnyDistinct() || isGroupQuery)) {
throw DbException.get(ErrorCode.FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT);
}
this.isForUpdate = b; this.isForUpdate = b;
if (session.getDatabase().getSettings().selectForUpdateMvcc && if (session.getDatabase().isMVStore()) {
session.getDatabase().isMVStore()) {
isForUpdateMvcc = b; isForUpdateMvcc = b;
} }
} }
......
...@@ -296,14 +296,6 @@ public class DbSettings extends SettingsBase { ...@@ -296,14 +296,6 @@ public class DbSettings extends SettingsBase {
*/ */
public final boolean rowId = get("ROWID", true); public final boolean rowId = get("ROWID", true);
/**
* Database setting <code>SELECT_FOR_UPDATE_MVCC</code>
* (default: true).<br />
* If set, SELECT .. FOR UPDATE queries lock only the selected rows when
* using MVCC.
*/
public final boolean selectForUpdateMvcc = get("SELECT_FOR_UPDATE_MVCC", true);
/** /**
* Database setting <code>SHARE_LINKED_CONNECTIONS</code> * Database setting <code>SHARE_LINKED_CONNECTIONS</code>
* (default: true).<br /> * (default: true).<br />
......
...@@ -566,6 +566,7 @@ public class DbException extends RuntimeException { ...@@ -566,6 +566,7 @@ public class DbException extends RuntimeException {
case CAN_ONLY_ASSIGN_TO_VARIABLE_1: case CAN_ONLY_ASSIGN_TO_VARIABLE_1:
case PUBLIC_STATIC_JAVA_METHOD_NOT_FOUND_1: case PUBLIC_STATIC_JAVA_METHOD_NOT_FOUND_1:
case JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE: case JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE:
case FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT:
return new JdbcSQLSyntaxErrorException(message, sql, state, errorCode, cause, stackTrace); return new JdbcSQLSyntaxErrorException(message, sql, state, errorCode, cause, stackTrace);
case HEX_STRING_ODD_1: case HEX_STRING_ODD_1:
case HEX_STRING_WRONG_1: case HEX_STRING_WRONG_1:
......
...@@ -1419,7 +1419,8 @@ public class MVStore { ...@@ -1419,7 +1419,8 @@ public class MVStore {
Set<Long> inspectedRoots = new HashSet<>(); Set<Long> inspectedRoots = new HashSet<>();
do { do {
inspectVersion(rootReference, collector, executorService, executingThreadCounter, inspectedRoots); inspectVersion(rootReference, collector, executorService, executingThreadCounter, inspectedRoots);
} while (rootReference.version >= oldestVersionToKeep && (rootReference = rootReference.previous) != null); } while (rootReference.version >= oldestVersionToKeep
&& (rootReference = rootReference.previous) != null);
} }
return collector.getReferenced(); return collector.getReferenced();
} finally { } finally {
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=#Step size must not be zero 90142=#Step size must not be zero
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=Obecná chyba: {0} HY000=Obecná chyba: {0}
HY004=Neznámý datový typ: {0} HY004=Neznámý datový typ: {0}
HYC00=Vlastnost není podporována: {0} HYC00=Vlastnost není podporována: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=Schrittgrösse darf nicht 0 sein 90142=Schrittgrösse darf nicht 0 sein
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=Allgemeiner Fehler: {0} HY000=Allgemeiner Fehler: {0}
HY004=Unbekannter Datentyp: {0} HY004=Unbekannter Datentyp: {0}
HYC00=Dieses Feature wird nicht unterstützt: {0} HYC00=Dieses Feature wird nicht unterstützt: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=Step size must not be zero 90142=Step size must not be zero
90143=Row {1} not found in primary index {0} 90143=Row {1} not found in primary index {0}
90144=Authenticator not enabled on database {0} 90144=Authenticator not enabled on database {0}
90145=FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=General error: {0} HY000=General error: {0}
HY004=Unknown data type: {0} HY004=Unknown data type: {0}
HYC00=Feature not supported: {0} HYC00=Feature not supported: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=#Step size must not be zero 90142=#Step size must not be zero
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=Error General : {0} HY000=Error General : {0}
HY004=Tipo de dato desconocido : {0} HY004=Tipo de dato desconocido : {0}
HYC00=Caracteristica no soportada: {0} HYC00=Caracteristica no soportada: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=La taille de l''étape ne doit pas être de 0 90142=La taille de l''étape ne doit pas être de 0
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=Erreur générale: {0} HY000=Erreur générale: {0}
HY004=Type de données inconnu: {0} HY004=Type de données inconnu: {0}
HYC00=Fonctionnalité non supportée: {0} HYC00=Fonctionnalité non supportée: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=ステップサイズに0は指定できません 90142=ステップサイズに0は指定できません
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=一般エラー: {0} HY000=一般エラー: {0}
HY004=不明なデータ型: {0} HY004=不明なデータ型: {0}
HYC00=機能はサポートされていません: {0} HYC00=機能はサポートされていません: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=#Step size must not be zero 90142=#Step size must not be zero
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=Błąd ogólny: {0} HY000=Błąd ogólny: {0}
HY004=Nieznany typ danych: {0} HY004=Nieznany typ danych: {0}
HYC00=Cecha nie jest wspierana: {0} HYC00=Cecha nie jest wspierana: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=#Step size must not be zero 90142=#Step size must not be zero
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=Erro geral: {0} HY000=Erro geral: {0}
HY004=Tipo de dados desconhecido: {0} HY004=Tipo de dados desconhecido: {0}
HYC00=Recurso não suportado: {0} HYC00=Recurso não suportado: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=Размер шага не должен быть равен нулю 90142=Размер шага не должен быть равен нулю
90143=Строка {1} не найдена в первичном индексе {0} 90143=Строка {1} не найдена в первичном индексе {0}
90144=Внешняя аутентификация не включена в базе данных {0} 90144=Внешняя аутентификация не включена в базе данных {0}
90145=FOR UPDATE не допускается в запросе с DISTINCT или запросе с группировкой
HY000=Внутренняя ошибка: {0} HY000=Внутренняя ошибка: {0}
HY004=Неизвестный тип данных: {0} HY004=Неизвестный тип данных: {0}
HYC00=Данная функция не поддерживается: {0} HYC00=Данная функция не поддерживается: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=#Step size must not be zero 90142=#Step size must not be zero
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=Všeobecná chyba: {0} HY000=Všeobecná chyba: {0}
HY004=Neznámy dátový typ: {0} HY004=Neznámy dátový typ: {0}
HYC00=Vlastnosť nie je podporovaná: {0} HYC00=Vlastnosť nie je podporovaná: {0}
......
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
90142=#Step size must not be zero 90142=#Step size must not be zero
90143=#Row {1} not found in primary index {0} 90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0} 90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
HY000=常规错误: {0} HY000=常规错误: {0}
HY004=位置数据类型: {0} HY004=位置数据类型: {0}
HYC00=不支持的特性: {0} HYC00=不支持的特性: {0}
......
...@@ -280,7 +280,7 @@ public class TestCases extends TestDb { ...@@ -280,7 +280,7 @@ public class TestCases extends TestDb {
stat.execute("create table test(id int primary key)"); stat.execute("create table test(id int primary key)");
assertThrows(ErrorCode.COLUMN_IS_REFERENCED_1, stat). assertThrows(ErrorCode.COLUMN_IS_REFERENCED_1, stat).
execute("alter table test alter column id " + execute("alter table test alter column id " +
"set default ifnull((select max(id) from test for update)+1, 0)"); "set default ifnull((select max(id) from test)+1, 0)");
stat.execute("drop table test"); stat.execute("drop table test");
conn.close(); conn.close();
} }
......
...@@ -158,17 +158,38 @@ public class TestTransaction extends TestDb { ...@@ -158,17 +158,38 @@ public class TestTransaction extends TestDb {
conn.setAutoCommit(false); conn.setAutoCommit(false);
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar)"); stat.execute("create table test(id int primary key, name varchar)");
stat.execute("create table test2(id int primary key, name varchar)");
stat.execute("insert into test values(1, 'Hello'), (2, 'World')"); stat.execute("insert into test values(1, 'Hello'), (2, 'World')");
stat.execute("insert into test2 values(1, 'A'), (2, 'B')");
conn.commit(); conn.commit();
PreparedStatement prep = conn.prepareStatement( testConcurrentSelectForUpdateImpl(conn, "*");
"select * from test for update"); testConcurrentSelectForUpdateImpl(conn, "*, count(*) over ()");
conn.close();
}
private void testConcurrentSelectForUpdateImpl(Connection conn, String expressions) throws SQLException {
Connection conn2;
PreparedStatement prep;
prep = conn.prepareStatement("select * from test for update");
prep.execute(); prep.execute();
Connection conn2 = getConnection("transaction"); conn2 = getConnection("transaction");
conn2.setAutoCommit(false);
assertThrows(ErrorCode.LOCK_TIMEOUT_1, conn2.createStatement()).
execute("select " + expressions + " from test for update");
conn2.close();
conn.commit();
prep = conn.prepareStatement("select " + expressions
+ " from test join test2 on test.id = test2.id for update");
prep.execute();
conn2 = getConnection("transaction");
conn2.setAutoCommit(false); conn2.setAutoCommit(false);
assertThrows(ErrorCode.LOCK_TIMEOUT_1, conn2.createStatement()). assertThrows(ErrorCode.LOCK_TIMEOUT_1, conn2.createStatement()).
execute("select * from test for update"); execute("select * from test for update");
assertThrows(ErrorCode.LOCK_TIMEOUT_1, conn2.createStatement()).
execute("select * from test2 for update");
conn2.close(); conn2.close();
conn.close(); conn.commit();
} }
private void testForUpdate() throws SQLException { private void testForUpdate() throws SQLException {
......
...@@ -126,22 +126,14 @@ public class TestMvcc2 extends TestDb { ...@@ -126,22 +126,14 @@ public class TestMvcc2 extends TestDb {
} }
private void testSelectForUpdate() throws SQLException { private void testSelectForUpdate() throws SQLException {
Connection conn = getConnection("mvcc2;SELECT_FOR_UPDATE_MVCC=true"); Connection conn = getConnection("mvcc2");
Connection conn2 = getConnection("mvcc2;SELECT_FOR_UPDATE_MVCC=true"); Connection conn2 = getConnection("mvcc2");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar)"); stat.execute("create table test(id int primary key, name varchar)");
conn.setAutoCommit(false); conn.setAutoCommit(false);
stat.execute("insert into test select x, 'Hello' from system_range(1, 10)"); stat.execute("insert into test select x, 'Hello' from system_range(1, 10)");
stat.execute("select * from test where id = 3 for update"); stat.execute("select * from test where id = 3 for update");
conn.commit(); conn.commit();
assertThrows(ErrorCode.FEATURE_NOT_SUPPORTED_1, stat).
execute("select sum(id) from test for update");
assertThrows(ErrorCode.FEATURE_NOT_SUPPORTED_1, stat).
execute("select distinct id from test for update");
assertThrows(ErrorCode.FEATURE_NOT_SUPPORTED_1, stat).
execute("select id from test group by id for update");
assertThrows(ErrorCode.FEATURE_NOT_SUPPORTED_1, stat).
execute("select t1.id from test t1, test t2 for update");
stat.execute("select * from test where id = 3 for update"); stat.execute("select * from test where id = 3 for update");
conn2.setAutoCommit(false); conn2.setAutoCommit(false);
conn2.createStatement().execute("select * from test where id = 4 for update"); conn2.createStatement().execute("select * from test where id = 4 for update");
......
...@@ -407,3 +407,35 @@ SELECT * EXCEPT (T1.A, T2.D) FROM TEST1 T1, TEST2 T2; ...@@ -407,3 +407,35 @@ SELECT * EXCEPT (T1.A, T2.D) FROM TEST1 T1, TEST2 T2;
DROP TABLE TEST1, TEST2; DROP TABLE TEST1, TEST2;
> ok > ok
CREATE TABLE TEST(ID INT PRIMARY KEY, VALUE INT NOT NULL);
> ok
INSERT INTO TEST VALUES (1, 1), (2, 1), (3, 2);
> update count: 3
SELECT ID, VALUE FROM TEST FOR UPDATE;
> ID VALUE
> -- -----
> 1 1
> 2 1
> 3 2
> rows: 3
SELECT DISTINCT VALUE FROM TEST FOR UPDATE;
> exception FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT
SELECT DISTINCT ON(VALUE) ID, VALUE FROM TEST FOR UPDATE;
> exception FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT
SELECT SUM(VALUE) FROM TEST FOR UPDATE;
> exception FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT
SELECT ID FROM TEST GROUP BY VALUE FOR UPDATE;
> exception FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT
SELECT 1 FROM TEST HAVING TRUE FOR UPDATE;
> exception FOR_UPDATE_IS_NOT_ALLOWED_IN_DISTINCT_OR_GROUPED_SELECT
DROP TABLE TEST;
> ok
...@@ -801,4 +801,4 @@ partitioned tri partitions ...@@ -801,4 +801,4 @@ partitioned tri partitions
discard enhancements nolock surefire logarithm discard enhancements nolock surefire logarithm
qualification opportunity jumping exploited unacceptable vrs duplicated qualification opportunity jumping exploited unacceptable vrs duplicated
queryparser tokenized freeze factorings recompilation unenclosed queryparser tokenized freeze factorings recompilation unenclosed rfe dsync
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论