提交 59ed4567 authored 作者: Thomas Mueller's avatar Thomas Mueller

Page store: large values in indexed columns could corrupt the index.

上级 64b9cd64
......@@ -140,7 +140,6 @@ public abstract class PageBtree extends Page {
* @param row the row to add
* @return the split point of this page, or -1 if no split is required
*/
abstract int addRowTry(SearchRow row) throws SQLException;
/**
......
......@@ -91,11 +91,15 @@ public class PageBtreeLeaf extends PageBtree {
}
int addRowTry(SearchRow row) throws SQLException {
return addRow(row, true);
}
private int addRow(SearchRow row, boolean tryOnly) throws SQLException {
int rowLength = index.getRowSize(data, row, onlyPosition);
int pageSize = index.getPageStore().getPageSize();
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
if (last - rowLength < start + OFFSET_LENGTH) {
if (entryCount > 1) {
if (tryOnly && entryCount > 1) {
int x = find(row, false, true, true);
if (entryCount < 5) {
// required, otherwise the index doesn't work correctly
......@@ -107,6 +111,7 @@ public class PageBtreeLeaf extends PageBtree {
int third = entryCount / 3;
return x < third ? third : x >= 2 * third ? 2 * third : x;
}
readAllRows();
onlyPosition = true;
// change the offsets (now storing only positions)
int o = pageSize;
......@@ -151,7 +156,7 @@ public class PageBtreeLeaf extends PageBtree {
return -1;
}
private void removeRow(int i) throws SQLException {
private void removeRow(int at) throws SQLException {
readAllRows();
index.getPageStore().logUndo(this, data);
entryCount--;
......@@ -161,14 +166,14 @@ public class PageBtreeLeaf extends PageBtree {
}
int[] newOffsets = new int[entryCount];
SearchRow[] newRows = new SearchRow[entryCount];
System.arraycopy(offsets, 0, newOffsets, 0, i);
System.arraycopy(rows, 0, newRows, 0, i);
int startNext = i > 0 ? offsets[i - 1] : index.getPageStore().getPageSize();
int rowLength = startNext - offsets[i];
for (int j = i; j < entryCount; j++) {
System.arraycopy(offsets, 0, newOffsets, 0, at);
System.arraycopy(rows, 0, newRows, 0, at);
int startNext = at > 0 ? offsets[at - 1] : index.getPageStore().getPageSize();
int rowLength = startNext - offsets[at];
for (int j = at; j < entryCount; j++) {
newOffsets[j] = offsets[j + 1] + rowLength;
}
System.arraycopy(rows, i + 1, newRows, i, entryCount - i);
System.arraycopy(rows, at + 1, newRows, at, entryCount - at);
start -= OFFSET_LENGTH;
offsets = newOffsets;
rows = newRows;
......@@ -182,7 +187,7 @@ public class PageBtreeLeaf extends PageBtree {
int newPageId = index.getPageStore().allocatePage();
PageBtreeLeaf p2 = PageBtreeLeaf.create(index, newPageId, parentPageId);
for (int i = splitPoint; i < entryCount;) {
p2.addRowTry(getRow(splitPoint));
p2.addRow(getRow(splitPoint), false);
removeRow(splitPoint);
}
return p2;
......
......@@ -143,6 +143,7 @@ public class PageBtreeNode extends PageBtree {
int pageSize = index.getPageStore().getPageSize();
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
if (last - rowLength < start + CHILD_OFFSET_PAIR_LENGTH) {
readAllRows();
onlyPosition = true;
// change the offsets (now storing only positions)
int o = pageSize;
......
......@@ -122,6 +122,9 @@ public class PageDataLeaf extends PageData {
keys = new long[entryCount];
rows = new Row[entryCount];
if (type == Page.TYPE_DATA_LEAF) {
if (entryCount != 1) {
Message.throwInternalError("entries: " + entryCount);
}
firstOverflowPageId = data.readInt();
}
for (int i = 0; i < entryCount; i++) {
......@@ -256,6 +259,9 @@ public class PageDataLeaf extends PageData {
if (entryCount < 0) {
Message.throwInternalError();
}
firstOverflowPageId = 0;
overflowRowSize = 0;
rowRef = null;
int keyOffsetPairLen = 2 + data.getVarLongLen(keys[i]);
int[] newOffsets = new int[entryCount];
long[] newKeys = new long[entryCount];
......@@ -333,7 +339,10 @@ public class PageDataLeaf extends PageData {
int newPageId = index.getPageStore().allocatePage();
PageDataLeaf p2 = PageDataLeaf.create(index, newPageId, parentPageId);
for (int i = splitPoint; i < entryCount;) {
p2.addRowTry(getRowAt(splitPoint));
int split = p2.addRowTry(getRowAt(splitPoint));
if (split != -1) {
Message.throwInternalError("split " + split);
}
removeRow(splitPoint);
}
return p2;
......
......@@ -7,6 +7,7 @@
package org.h2.test.unit;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
......@@ -32,6 +33,8 @@ public class TestPageStore extends TestBase {
}
public void test() throws Exception {
testExistingOld();
testLargeRows();
testRecoverDropIndex();
testDropPk();
testCreatePkLater();
......@@ -42,6 +45,110 @@ public class TestPageStore extends TestBase {
testFuzzOperations();
}
private void testExistingOld() throws SQLException {
if (config.memory) {
return;
}
Connection conn;
deleteDb("pageStore");
String url;
url = "jdbc:h2:" + baseDir + "/pageStore";
conn = DriverManager.getConnection(url + ";PAGE_STORE=FALSE");
conn.createStatement().execute("create table test(id int) as select 1");
conn.close();
conn = DriverManager.getConnection(url);
conn.createStatement().execute("select * from test");
conn.close();
conn = DriverManager.getConnection(url + ";PAGE_STORE=TRUE");
conn.createStatement().execute("create table test(id int) as select 2");
conn.close();
conn = DriverManager.getConnection(url);
this.assertResult("2", conn.createStatement(), "select * from test");
conn.close();
}
private void testLargeRows() throws Exception {
if (config.memory) {
return;
}
for (int i = 0; i < 10; i++) {
testLargeRows(i);
}
}
private void testLargeRows(int seed) throws Exception {
deleteDb("pageStore");
String url = getURL("pageStore;CACHE_SIZE=16", true);
Connection conn = null;
Statement stat = null;
int count = 0;
try {
Class.forName("org.h2.Driver");
conn = DriverManager.getConnection(url);
stat = conn.createStatement();
int tableCount = 1;
PreparedStatement[] insert = new PreparedStatement[tableCount];
PreparedStatement[] deleteMany = new PreparedStatement[tableCount];
PreparedStatement[] updateMany = new PreparedStatement[tableCount];
for (int i = 0; i < tableCount; i++) {
stat.execute("create table test" + i + "(id int primary key, name varchar)");
stat.execute("create index idx_test" + i + " on test" + i + "(name)");
insert[i] = conn.prepareStatement("insert into test" + i + " values(?, ? || space(?))");
deleteMany[i] = conn.prepareStatement("delete from test" + i + " where id between ? and ?");
updateMany[i] = conn.prepareStatement("update test" + i + " set name=? || space(?) where id between ? and ?");
}
Random random = new Random(seed);
for (int i = 0; i < 1000; i++) {
count = i;
PreparedStatement p;
if (random.nextInt(100) < 95) {
p = insert[random.nextInt(tableCount)];
p.setInt(1, i);
p.setInt(2, i);
if (random.nextInt(30) == 5) {
p.setInt(3, 3000);
} else {
p.setInt(3, random.nextInt(100));
}
p.execute();
} else if (random.nextInt(100) < 90) {
p = updateMany[random.nextInt(tableCount)];
p.setInt(1, i);
p.setInt(2, random.nextInt(50));
int start = random.nextInt(1 + i);
p.setInt(3, start);
p.setInt(4, start + random.nextInt(50));
p.executeUpdate();
} else {
p = deleteMany[random.nextInt(tableCount)];
int start = random.nextInt(1 + i);
p.setInt(1, start);
p.setInt(2, start + random.nextInt(100));
p.executeUpdate();
}
}
conn.close();
conn = DriverManager.getConnection(url);
conn.close();
conn = DriverManager.getConnection(url);
stat = conn.createStatement();
stat.execute("script to '" + baseDir + "/pageStore.sql'");
conn.close();
} catch (Exception e) {
try {
stat.execute("shutdown immediately");
} catch (SQLException e2) {
// ignore
}
try {
conn.close();
} catch (SQLException e2) {
// ignore
}
fail("count: " + count + " " + e);
}
}
private void testRecoverDropIndex() throws SQLException {
if (config.memory) {
return;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论