提交 83705eb0 authored 作者: Thomas Mueller's avatar Thomas Mueller

IN(..): when combining and IN(..) condition with a condition that didn't use an…

IN(..): when combining and IN(..) condition with a condition that didn't use an index, the result could contain duplicate rows.
上级 d2f528bb
......@@ -19,7 +19,9 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul><li>EXPLAIN: The query plan now contains ".tableScan" if a table scan is used.
</li><li>The index was not used if there were multiple IN(..) conditions.
</li><li>IN(..): when combining and IN(..) condition with a condition that didn't use an index,
the result could contain duplicate rows.
</li><li>IN(..): the index was not used if there were multiple IN(..) conditions.
</li><li>For foreign key constraints, the metadata column
INFORMATION_SCHEMA.INDEXES.CONSTRAINT_NAME was not set for non-unique indexes.
</li><li>PostgreSQL compatibility: when using the following exception was thrown:
......
......@@ -20,6 +20,7 @@ import org.h2.expression.ExpressionVisitor;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.StatementBuilder;
import org.h2.value.CompareMode;
import org.h2.value.Value;
......@@ -219,28 +220,16 @@ public class IndexCondition {
case Comparison.IN_LIST:
case Comparison.IN_QUERY:
if (indexConditions.size() > 1) {
// IN(..) can not be combined with other conditions.
// If there are other conditions (except for other
// IN_ conditions), don't use the index on IN(..)
// otherwise the query returns the wrong result.
boolean beforeThis = true;
for (IndexCondition c : indexConditions) {
if (c == this) {
beforeThis = false;
continue;
}
if (c.isEvaluatable()) {
if (beforeThis) {
// If there are only multiple IN_ conditions,
// only the first one can be used
return 0;
}
if (!c.isIn()) {
// If there are other (non-IN_) conditions,
// this one can't be used
return 0;
}
}
if (!column.getTable().getTableType().equals(Table.TABLE)) {
// if combined with other conditions,
// IN(..) can only be used for regular tables
// test case:
// create table test(a int, b int, primary key(id, name));
// create unique index c on test(b, a);
// insert into test values(1, 10), (2, 20);
// select * from (select * from test)
// where a=1 and b in(10, 20);
return 0;
}
}
return EQUALITY;
......@@ -264,16 +253,6 @@ public class IndexCondition {
return compareType == Comparison.FALSE;
}
private boolean isIn() {
switch (compareType) {
case Comparison.IN_LIST:
case Comparison.IN_QUERY:
return true;
default:
return false;
}
}
/**
* Check if this index condition is of the type column larger or equal to
* value.
......
......@@ -249,7 +249,10 @@ public class PageDataIndex extends PageIndex {
}
public Cursor find(Session session, SearchRow first, SearchRow last) {
// ignore first and last
if (first != null || last != null) {
// this index is a table scan, must not use it for lookup
throw DbException.throwInternalError(getSQL() + " " + first + " " + last);
}
PageData root = getPage(rootPageId, 0);
return root.find(session, Long.MIN_VALUE, Long.MAX_VALUE, isMultiVersion);
}
......@@ -422,9 +425,7 @@ public class PageDataIndex extends PageIndex {
}
public int getColumnIndex(Column col) {
if (col.getColumnId() == mainIndexColumn) {
return 0;
}
// can not use this index - use the PageDelegateIndex instead
return -1;
}
......
......@@ -75,7 +75,10 @@ public class PageDelegateIndex extends PageIndex {
}
public int getColumnIndex(Column col) {
return mainIndex.getColumnIndex(col);
if (col.getColumnId() == mainIndex.getMainIndexColumn()) {
return 0;
}
return -1;
}
public double getCost(Session session, int[] masks) {
......
......@@ -9,7 +9,6 @@ package org.h2.table;
import java.sql.ResultSetMetaData;
import java.sql.Time;
import java.sql.Timestamp;
import org.h2.command.Parser;
import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
create table test(id int primary key, d int);
> ok
insert into test values(1,1), (2, 1);
> update count: 2
select id from test where id in (1, 2) and d = 1;
> ID
> --
> 1
> 2
> rows: 2
drop table test;
> ok
create table test(id decimal(10, 2) primary key) as select 0;
> ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论