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

Merge pull request #1707 from katzyn/result

Fix sort order and ENUM data type in external results
...@@ -21,6 +21,8 @@ Change Log ...@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>PR #1707: Fix sort order and ENUM data type in external results
</li>
<li>PR #1706: Add hypothetical set functions <li>PR #1706: Add hypothetical set functions
</li> </li>
<li>PR #1705: Fix GROUP_CONCAT with variable separator <li>PR #1705: Fix GROUP_CONCAT with variable separator
......
...@@ -59,8 +59,8 @@ class MVPlainTempResult extends MVTempResult { ...@@ -59,8 +59,8 @@ class MVPlainTempResult extends MVTempResult {
* count of visible columns * count of visible columns
*/ */
MVPlainTempResult(Database database, Expression[] expressions, int visibleColumnCount) { MVPlainTempResult(Database database, Expression[] expressions, int visibleColumnCount) {
super(database, expressions.length, visibleColumnCount); super(database, expressions, visibleColumnCount);
ValueDataType valueType = new ValueDataType(database, new int[columnCount]); ValueDataType valueType = new ValueDataType(database, new int[expressions.length]);
Builder<Long, ValueRow> builder = new MVMap.Builder<Long, ValueRow>() Builder<Long, ValueRow> builder = new MVMap.Builder<Long, ValueRow>()
.valueType(valueType).singleWriter(); .valueType(valueType).singleWriter();
map = store.openMap("tmp", builder); map = store.openMap("tmp", builder);
...@@ -99,7 +99,11 @@ class MVPlainTempResult extends MVTempResult { ...@@ -99,7 +99,11 @@ class MVPlainTempResult extends MVTempResult {
return null; return null;
} }
cursor.next(); cursor.next();
return cursor.getValue().getList(); Value[] currentRow = cursor.getValue().getList();
if (hasEnum) {
fixEnum(currentRow);
}
return currentRow;
} }
@Override @Override
......
...@@ -109,10 +109,10 @@ class MVSortedTempResult extends MVTempResult { ...@@ -109,10 +109,10 @@ class MVSortedTempResult extends MVTempResult {
*/ */
MVSortedTempResult(Database database, Expression[] expressions, boolean distinct, int[] distinctIndexes, MVSortedTempResult(Database database, Expression[] expressions, boolean distinct, int[] distinctIndexes,
int visibleColumnCount, SortOrder sort) { int visibleColumnCount, SortOrder sort) {
super(database, expressions.length, visibleColumnCount); super(database, expressions, visibleColumnCount);
this.distinct = distinct; this.distinct = distinct;
this.distinctIndexes = distinctIndexes; this.distinctIndexes = distinctIndexes;
int length = columnCount; int length = expressions.length;
int[] sortTypes = new int[length]; int[] sortTypes = new int[length];
int[] indexes; int[] indexes;
if (sort != null) { if (sort != null) {
...@@ -192,7 +192,7 @@ class MVSortedTempResult extends MVTempResult { ...@@ -192,7 +192,7 @@ class MVSortedTempResult extends MVTempResult {
if (index.putIfAbsent(distinctRow, true) != null) { if (index.putIfAbsent(distinctRow, true) != null) {
return rowCount; return rowCount;
} }
} else if (columnCount != visibleColumnCount) { } else if (expressions.length != visibleColumnCount) {
ValueRow distinctRow = ValueRow.get(Arrays.copyOf(values, visibleColumnCount)); ValueRow distinctRow = ValueRow.get(Arrays.copyOf(values, visibleColumnCount));
if (index.putIfAbsent(distinctRow, true) != null) { if (index.putIfAbsent(distinctRow, true) != null) {
return rowCount; return rowCount;
...@@ -221,7 +221,7 @@ class MVSortedTempResult extends MVTempResult { ...@@ -221,7 +221,7 @@ class MVSortedTempResult extends MVTempResult {
return parent.contains(values); return parent.contains(values);
} }
assert distinct; assert distinct;
if (columnCount != visibleColumnCount) { if (expressions.length != visibleColumnCount) {
return index.containsKey(ValueRow.get(values)); return index.containsKey(ValueRow.get(values));
} }
return map.containsKey(getKey(values)); return map.containsKey(getKey(values));
...@@ -297,6 +297,9 @@ class MVSortedTempResult extends MVTempResult { ...@@ -297,6 +297,9 @@ class MVSortedTempResult extends MVTempResult {
} }
// Read the next row // Read the next row
current = getValue(cursor.next().getList()); current = getValue(cursor.next().getList());
if (hasEnum) {
fixEnum(current);
}
/* /*
* If valueCount is greater than 1 that is possible for non-distinct results the * If valueCount is greater than 1 that is possible for non-distinct results the
* following invocations of next() will use this.current and this.valueCount. * following invocations of next() will use this.current and this.valueCount.
...@@ -308,7 +311,7 @@ class MVSortedTempResult extends MVTempResult { ...@@ -308,7 +311,7 @@ class MVSortedTempResult extends MVTempResult {
@Override @Override
public int removeRow(Value[] values) { public int removeRow(Value[] values) {
assert parent == null && distinct; assert parent == null && distinct;
if (columnCount != visibleColumnCount) { if (expressions.length != visibleColumnCount) {
throw DbException.getUnsupportedException("removeRow()"); throw DbException.getUnsupportedException("removeRow()");
} }
// If an entry was removed decrement the counter // If an entry was removed decrement the counter
......
...@@ -19,6 +19,7 @@ import org.h2.result.ResultExternal; ...@@ -19,6 +19,7 @@ import org.h2.result.ResultExternal;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.util.TempFileDeleter; import org.h2.util.TempFileDeleter;
import org.h2.value.TypeInfo;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
...@@ -87,15 +88,17 @@ public abstract class MVTempResult implements ResultExternal { ...@@ -87,15 +88,17 @@ public abstract class MVTempResult implements ResultExternal {
final MVStore store; final MVStore store;
/** /**
* Count of columns. * Column expressions.
*/ */
final int columnCount; final Expression[] expressions;
/** /**
* Count of visible columns. * Count of visible columns.
*/ */
final int visibleColumnCount; final int visibleColumnCount;
final boolean hasEnum;
/** /**
* Count of rows. Used only in a root results, copies always have 0 value. * Count of rows. Used only in a root results, copies always have 0 value.
*/ */
...@@ -140,8 +143,9 @@ public abstract class MVTempResult implements ResultExternal { ...@@ -140,8 +143,9 @@ public abstract class MVTempResult implements ResultExternal {
MVTempResult(MVTempResult parent) { MVTempResult(MVTempResult parent) {
this.parent = parent; this.parent = parent;
this.store = parent.store; this.store = parent.store;
this.columnCount = parent.columnCount; this.expressions = parent.expressions;
this.visibleColumnCount = parent.visibleColumnCount; this.visibleColumnCount = parent.visibleColumnCount;
this.hasEnum = parent.hasEnum;
this.tempFileDeleter = null; this.tempFileDeleter = null;
this.closeable = null; this.closeable = null;
this.fileRef = null; this.fileRef = null;
...@@ -152,12 +156,12 @@ public abstract class MVTempResult implements ResultExternal { ...@@ -152,12 +156,12 @@ public abstract class MVTempResult implements ResultExternal {
* *
* @param database * @param database
* database * database
* @param columnCount * @param expressions
* count of columns * column expressions
* @param visibleColumnCount * @param visibleColumnCount
* count of visible columns * count of visible columns
*/ */
MVTempResult(Database database, int columnCount, int visibleColumnCount) { MVTempResult(Database database, Expression[] expressions, int visibleColumnCount) {
try { try {
String fileName = FileUtils.createTempFile("h2tmp", Constants.SUFFIX_TEMP_FILE, true); String fileName = FileUtils.createTempFile("h2tmp", Constants.SUFFIX_TEMP_FILE, true);
Builder builder = new MVStore.Builder().fileName(fileName).cacheSize(0).autoCommitDisabled(); Builder builder = new MVStore.Builder().fileName(fileName).cacheSize(0).autoCommitDisabled();
...@@ -166,8 +170,16 @@ public abstract class MVTempResult implements ResultExternal { ...@@ -166,8 +170,16 @@ public abstract class MVTempResult implements ResultExternal {
builder.encryptionKey(MVTableEngine.decodePassword(key)); builder.encryptionKey(MVTableEngine.decodePassword(key));
} }
store = builder.open(); store = builder.open();
this.columnCount = columnCount; this.expressions = expressions;
this.visibleColumnCount = visibleColumnCount; this.visibleColumnCount = visibleColumnCount;
boolean hasEnum = false;
for (Expression e : expressions) {
if (e.getType().getValueType() == Value.ENUM) {
hasEnum = true;
break;
}
}
this.hasEnum = hasEnum;
tempFileDeleter = database.getTempFileDeleter(); tempFileDeleter = database.getTempFileDeleter();
closeable = new CloseImpl(store, fileName); closeable = new CloseImpl(store, fileName);
fileRef = tempFileDeleter.addFile(closeable, this); fileRef = tempFileDeleter.addFile(closeable, this);
...@@ -210,4 +222,13 @@ public abstract class MVTempResult implements ResultExternal { ...@@ -210,4 +222,13 @@ public abstract class MVTempResult implements ResultExternal {
tempFileDeleter.deleteFile(fileRef, closeable); tempFileDeleter.deleteFile(fileRef, closeable);
} }
final void fixEnum(Value[] row) {
for (int i = 0, l = expressions.length; i < l; i++) {
TypeInfo type = expressions[i].getType();
if (type.getValueType() == Value.ENUM) {
row[i] = type.getExtTypeInfo().cast(row[i]);
}
}
}
} }
...@@ -136,9 +136,9 @@ public class ValueDataType implements DataType { ...@@ -136,9 +136,9 @@ public class ValueDataType implements DataType {
if (a == b) { if (a == b) {
return 0; return 0;
} }
if (a instanceof ValueArray && b instanceof ValueArray) { if (a instanceof ValueCollectionBase && b instanceof ValueCollectionBase) {
Value[] ax = ((ValueArray) a).getList(); Value[] ax = ((ValueCollectionBase) a).getList();
Value[] bx = ((ValueArray) b).getList(); Value[] bx = ((ValueCollectionBase) b).getList();
int al = ax.length; int al = ax.length;
int bl = bx.length; int bl = bx.length;
int len = Math.min(al, bl); int len = Math.min(al, bl);
......
...@@ -928,7 +928,6 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -928,7 +928,6 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestCluster()); addTest(new TestCluster());
addTest(new TestFileLockSerialized()); addTest(new TestFileLockSerialized());
addTest(new TestFileLockProcess()); addTest(new TestFileLockProcess());
addTest(new TestFileSystem());
addTest(new TestDefrag()); addTest(new TestDefrag());
addTest(new TestTools()); addTest(new TestTools());
addTest(new TestSampleApps()); addTest(new TestSampleApps());
...@@ -973,6 +972,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -973,6 +972,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestDateIso8601()); addTest(new TestDateIso8601());
addTest(new TestDbException()); addTest(new TestDbException());
addTest(new TestFile()); addTest(new TestFile());
addTest(new TestFileSystem());
addTest(new TestFtp()); addTest(new TestFtp());
addTest(new TestGeometryUtils()); addTest(new TestGeometryUtils());
addTest(new TestInterval()); addTest(new TestInterval());
......
...@@ -16,6 +16,7 @@ import java.nio.channels.FileChannel.MapMode; ...@@ -16,6 +16,7 @@ import java.nio.channels.FileChannel.MapMode;
import java.nio.channels.FileLock; import java.nio.channels.FileLock;
import java.nio.channels.NonWritableChannelException; import java.nio.channels.NonWritableChannelException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
...@@ -32,7 +33,6 @@ import org.h2.store.fs.FilePathEncrypt; ...@@ -32,7 +33,6 @@ import org.h2.store.fs.FilePathEncrypt;
import org.h2.store.fs.FilePathRec; import org.h2.store.fs.FilePathRec;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.TestDb;
import org.h2.test.utils.AssertThrows; import org.h2.test.utils.AssertThrows;
import org.h2.test.utils.FilePathDebug; import org.h2.test.utils.FilePathDebug;
import org.h2.tools.Backup; import org.h2.tools.Backup;
...@@ -43,7 +43,7 @@ import org.h2.util.Task; ...@@ -43,7 +43,7 @@ import org.h2.util.Task;
/** /**
* Tests various file system. * Tests various file system.
*/ */
public class TestFileSystem extends TestDb { public class TestFileSystem extends TestBase {
/** /**
* Run just this test. * Run just this test.
...@@ -251,7 +251,7 @@ public class TestFileSystem extends TestDb { ...@@ -251,7 +251,7 @@ public class TestFileSystem extends TestDb {
FileUtils.deleteRecursive(dir, false); FileUtils.deleteRecursive(dir, false);
Connection conn; Connection conn;
Statement stat; Statement stat;
conn = getConnection("jdbc:h2:split:18:"+dir+"/test"); conn = DriverManager.getConnection("jdbc:h2:split:18:"+dir+"/test");
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute( stat.execute(
"create table test(id int primary key, name varchar) " + "create table test(id int primary key, name varchar) " +
...@@ -260,7 +260,7 @@ public class TestFileSystem extends TestDb { ...@@ -260,7 +260,7 @@ public class TestFileSystem extends TestDb {
conn.close(); conn.close();
Backup.execute(dir + "/test.zip", dir, "", true); Backup.execute(dir + "/test.zip", dir, "", true);
DeleteDbFiles.execute("split:" + dir, "test", true); DeleteDbFiles.execute("split:" + dir, "test", true);
conn = getConnection( conn = DriverManager.getConnection(
"jdbc:h2:split:zip:"+dir+"/test.zip!/test"); "jdbc:h2:split:zip:"+dir+"/test.zip!/test");
conn.createStatement().execute("select * from test where id=1"); conn.createStatement().execute("select * from test where id=1");
conn.close(); conn.close();
...@@ -269,22 +269,22 @@ public class TestFileSystem extends TestDb { ...@@ -269,22 +269,22 @@ public class TestFileSystem extends TestDb {
private void testDatabaseInMemFileSys() throws SQLException { private void testDatabaseInMemFileSys() throws SQLException {
org.h2.Driver.load(); org.h2.Driver.load();
deleteDb("fsMem"); String dir = getBaseDir() + "/fsMem";
String url = "jdbc:h2:" + getBaseDir() + "/fsMem"; FileUtils.deleteRecursive(dir, false);
Connection conn = getConnection(url, "sa", "sa"); String url = "jdbc:h2:" + dir + "/fsMem";
Connection conn = DriverManager.getConnection(url, "sa", "sa");
conn.createStatement().execute( conn.createStatement().execute(
"CREATE TABLE TEST AS SELECT * FROM DUAL"); "CREATE TABLE TEST AS SELECT * FROM DUAL");
conn.createStatement().execute( conn.createStatement().execute(
"BACKUP TO '" + getBaseDir() + "/fsMem.zip'"); "BACKUP TO '" + getBaseDir() + "/fsMem.zip'");
conn.close(); conn.close();
org.h2.tools.Restore.main("-file", getBaseDir() + "/fsMem.zip", "-dir", org.h2.tools.Restore.main("-file", getBaseDir() + "/fsMem.zip", "-dir", "memFS:");
"memFS:"); conn = DriverManager.getConnection("jdbc:h2:memFS:fsMem", "sa", "sa");
conn = getConnection("jdbc:h2:memFS:fsMem", "sa", "sa");
ResultSet rs = conn.createStatement() ResultSet rs = conn.createStatement()
.executeQuery("SELECT * FROM TEST"); .executeQuery("SELECT * FROM TEST");
rs.close(); rs.close();
conn.close(); conn.close();
deleteDb("fsMem"); FileUtils.deleteRecursive(dir, false);
FileUtils.delete(getBaseDir() + "/fsMem.zip"); FileUtils.delete(getBaseDir() + "/fsMem.zip");
FileUtils.delete("memFS:fsMem.mv.db"); FileUtils.delete("memFS:fsMem.mv.db");
} }
...@@ -297,8 +297,9 @@ public class TestFileSystem extends TestDb { ...@@ -297,8 +297,9 @@ public class TestFileSystem extends TestDb {
return; return;
} }
org.h2.Driver.load(); org.h2.Driver.load();
String url = "jdbc:h2:" + getBaseDir() + "/fsJar"; String dir = getBaseDir() + "/fsJar";
Connection conn = getConnection(url, "sa", "sa"); String url = "jdbc:h2:" + dir + "/fsJar";
Connection conn = DriverManager.getConnection(url, "sa", "sa");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, " + stat.execute("create table test(id int primary key, " +
"name varchar, b blob, c clob)"); "name varchar, b blob, c clob)");
...@@ -310,12 +311,12 @@ public class TestFileSystem extends TestDb { ...@@ -310,12 +311,12 @@ public class TestFileSystem extends TestDb {
byte[] b1 = rs.getBytes(3); byte[] b1 = rs.getBytes(3);
String s1 = rs.getString(4); String s1 = rs.getString(4);
conn.close(); conn.close();
conn = getConnection(url, "sa", "sa"); conn = DriverManager.getConnection(url, "sa", "sa");
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute("backup to '" + getBaseDir() + "/fsJar.zip'"); stat.execute("backup to '" + getBaseDir() + "/fsJar.zip'");
conn.close(); conn.close();
deleteDb("fsJar"); FileUtils.deleteRecursive(dir, false);
for (String f : FileUtils.newDirectoryStream( for (String f : FileUtils.newDirectoryStream(
"zip:" + getBaseDir() + "/fsJar.zip")) { "zip:" + getBaseDir() + "/fsJar.zip")) {
assertFalse(FileUtils.isAbsolute(f)); assertFalse(FileUtils.isAbsolute(f));
...@@ -334,7 +335,7 @@ public class TestFileSystem extends TestDb { ...@@ -334,7 +335,7 @@ public class TestFileSystem extends TestDb {
testReadOnly(f); testReadOnly(f);
} }
String urlJar = "jdbc:h2:zip:" + getBaseDir() + "/fsJar.zip!/fsJar"; String urlJar = "jdbc:h2:zip:" + getBaseDir() + "/fsJar.zip!/fsJar";
conn = getConnection(urlJar, "sa", "sa"); conn = DriverManager.getConnection(urlJar, "sa", "sa");
stat = conn.createStatement(); stat = conn.createStatement();
rs = stat.executeQuery("select * from test"); rs = stat.executeQuery("select * from test");
rs.next(); rs.next();
......
...@@ -11,6 +11,7 @@ import java.sql.DriverManager; ...@@ -11,6 +11,7 @@ import java.sql.DriverManager;
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.BitSet;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
...@@ -53,16 +54,25 @@ public class TestMVTempResult extends TestBase { ...@@ -53,16 +54,25 @@ public class TestMVTempResult extends TestBase {
DeleteDbFiles.execute(dir, name, true); DeleteDbFiles.execute(dir, name, true);
try (Connection c = DriverManager.getConnection("jdbc:h2:" + dir + '/' + name)) { try (Connection c = DriverManager.getConnection("jdbc:h2:" + dir + '/' + name)) {
Statement s = c.createStatement(); Statement s = c.createStatement();
try (ResultSet rs = s.executeQuery("SELECT X, RAND() R FROM SYSTEM_RANGE(1, " + ROWS + ") ORDER BY R")) { s.execute("CREATE TABLE TEST(I BIGINT, E ENUM('a', 'b'))" //
for (int i = 1; i <= ROWS; i++) { + " AS SELECT X, 'a' FROM SYSTEM_RANGE(1, " + ROWS + ')');
try (ResultSet rs = s.executeQuery("SELECT I, E FROM TEST ORDER BY I DESC")) {
for (int i = ROWS; i > 0; i--) {
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(i, rs.getLong(1));
assertEquals("a", rs.getString(2));
} }
assertFalse(rs.next());
} }
try (ResultSet rs = s.executeQuery("SELECT X, RAND() FROM SYSTEM_RANGE(1, " + ROWS + ')')) { BitSet set = new BitSet(ROWS);
try (ResultSet rs = s.executeQuery("SELECT I, E FROM TEST")) {
for (int i = 1; i <= ROWS; i++) { for (int i = 1; i <= ROWS; i++) {
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(i, rs.getLong(1)); set.set((int) rs.getLong(1));
assertEquals("a", rs.getString(2));
} }
assertFalse(rs.next());
assertEquals(ROWS, set.cardinality());
} }
} }
DeleteDbFiles.execute(dir, name, true); DeleteDbFiles.execute(dir, name, true);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论