提交 3fc9d62a authored 作者: noelgrandin's avatar noelgrandin

Add new collation command SET BINARY_COLLATION UNSIGNED, helps with people…

Add new collation command SET BINARY_COLLATION UNSIGNED, helps with people testing BINARY columns in MySQL mode.
上级 002ebdc9
......@@ -1041,6 +1041,20 @@ This command is effective immediately, but does not commit an open transaction.
SET CLUSTER ''
"
"Commands (Other)","SET BINARY_COLLATION","
SET BINARY_COLLATION
{ UNSIGNED | SIGNED } ] }
","
Sets the collation used for comparing BINARY columns, the default is SIGNED.
This command can only be executed if there are no tables defined.
Admin rights are required to execute this command.
This command commits an open transaction.
This setting is persistent.
","
SET BINARY_COLLATION SIGNED
"
"Commands (Other)","SET COLLATION","
SET [ DATABASE ] COLLATION
{ OFF | collationName [ STRENGTH { PRIMARY | SECONDARY | TERTIARY | IDENTICAL } ] }
......
......@@ -23,6 +23,7 @@ Change Log
the recover tool).
</li><li>Support TRUNC(timestamp) for improved Oracle compatiblity.
</li><li>Add support for CREATE TABLE TEST (ID BIGSERIAL) for PostgreSQL compatibility. Patch from Jesse Long.
</li><li>Add new collation command SET BINARY_COLLATION UNSIGNED, helps with people testing BINARY columns in MySQL mode.
</li></ul>
<h2>Version 1.3.171 (2013-03-17)</h2>
......
......@@ -4513,6 +4513,9 @@ public class Parser {
} else if (readIf("COLLATION")) {
readIfEqualOrTo();
return parseSetCollation();
} else if (readIf("BINARY_COLLATION")) {
readIfEqualOrTo();
return parseSetBinaryCollation();
} else if (readIf("CLUSTER")) {
readIfEqualOrTo();
Set command = new Set(session, SetTypes.CLUSTER);
......@@ -4684,6 +4687,17 @@ public class Parser {
return command;
}
private Set parseSetBinaryCollation() {
Set command = new Set(session, SetTypes.BINARY_COLLATION);
String name = readAliasIdentifier();
command.setString(name);
if (equalsToken(name, CompareMode.UNSIGNED)
|| equalsToken(name, CompareMode.SIGNED)) {
return command;
}
throw DbException.getInvalidValueException("BINARY_COLLATION", name);
}
private RunScriptCommand parseRunScript() {
RunScriptCommand command = new RunScriptCommand(session);
read("FROM");
......
......@@ -111,10 +111,11 @@ public class Set extends Prepared {
if (table != null) {
throw DbException.get(ErrorCode.COLLATION_CHANGE_WITH_DATA_TABLE_1, table.getSQL());
}
final boolean binaryUnsigned = database.getCompareMode().isBinaryUnsigned();
CompareMode compareMode;
StringBuilder buff = new StringBuilder(stringValue);
if (stringValue.equals(CompareMode.OFF)) {
compareMode = CompareMode.getInstance(null, 0);
compareMode = CompareMode.getInstance(null, 0, binaryUnsigned);
} else {
int strength = getIntValue();
buff.append(" STRENGTH ");
......@@ -127,12 +128,31 @@ public class Set extends Prepared {
} else if (strength == Collator.TERTIARY) {
buff.append("TERTIARY");
}
compareMode = CompareMode.getInstance(stringValue, strength);
compareMode = CompareMode.getInstance(stringValue, strength, binaryUnsigned);
}
addOrUpdateSetting(name, buff.toString(), 0);
database.setCompareMode(compareMode);
break;
}
case SetTypes.BINARY_COLLATION: {
session.getUser().checkAdmin();
Table table = database.getFirstUserTable();
if (table != null) {
throw DbException.get(ErrorCode.COLLATION_CHANGE_WITH_DATA_TABLE_1, table.getSQL());
}
CompareMode currentMode = database.getCompareMode();
CompareMode newMode;
if (stringValue.equals(CompareMode.SIGNED)) {
newMode = CompareMode.getInstance(currentMode.getName(), currentMode.getStrength(), false);
} else if (stringValue.equals(CompareMode.UNSIGNED)) {
newMode = CompareMode.getInstance(currentMode.getName(), currentMode.getStrength(), true);
} else {
throw DbException.getInvalidValueException("BINARY_COLLATION", stringValue);
}
addOrUpdateSetting(name, stringValue, 0);
database.setCompareMode(newMode);
break;
}
case SetTypes.COMPRESS_LOB: {
session.getUser().checkAdmin();
int algo = CompressTool.getCompressAlgorithm(stringValue);
......
......@@ -199,6 +199,11 @@ public class SetTypes {
*/
public static final int REDO_LOG_BINARY = 37;
/**
* The type of a SET BINARY_COLLATION statement.
*/
public static final int BINARY_COLLATION = 38;
private static final ArrayList<String> TYPES = New.arrayList();
private SetTypes() {
......@@ -245,6 +250,7 @@ public class SetTypes {
list.add(VARIABLE, "@");
list.add(QUERY_TIMEOUT, "QUERY_TIMEOUT");
list.add(REDO_LOG_BINARY, "REDO_LOG_BINARY");
list.add(BINARY_COLLATION, "BINARY_COLLATION");
}
/**
......
......@@ -178,7 +178,7 @@ public class Database implements DataHandler {
String name = ci.getName();
this.dbSettings = ci.getDbSettings();
this.reconnectCheckDelay = dbSettings.reconnectCheckDelay;
this.compareMode = CompareMode.getInstance(null, 0);
this.compareMode = CompareMode.getInstance(null, 0, false);
this.persistent = ci.isPersistent();
this.filePasswordHash = ci.getFilePasswordHash();
this.databaseName = name;
......
......@@ -82,7 +82,7 @@ public class JdbcConnection extends TraceObject implements Connection {
private int savepointId;
private String catalog;
private Statement executingStatement;
private final CompareMode compareMode = CompareMode.getInstance(null, 0);
private final CompareMode compareMode = CompareMode.getInstance(null, 0, false);
private final CloseWatcher watcher;
private int queryTimeoutCache = -1;
......
......@@ -1610,10 +1610,9 @@ public class PageStore implements CacheWriter {
int type = row.getValue(1).getInt();
int parent = row.getValue(2).getInt();
int rootPageId = row.getValue(3).getInt();
String options = row.getValue(4).getString();
String[] options = StringUtils.arraySplit(row.getValue(4).getString(), ',', false);
String columnList = row.getValue(5).getString();
String[] columns = StringUtils.arraySplit(columnList, ',', false);
String[] ops = StringUtils.arraySplit(options, ',', false);
Index meta;
if (trace.isDebugEnabled()) {
trace.debug("addMeta id="+ id +" type=" + type +
......@@ -1639,13 +1638,17 @@ public class PageStore implements CacheWriter {
data.schema = metaSchema;
data.tableName = "T" + id;
data.id = id;
data.temporary = ops[2].equals("temp");
data.temporary = options[2].equals("temp");
data.persistData = true;
data.persistIndexes = true;
data.create = false;
data.session = session;
RegularTable table = new RegularTable(data);
CompareMode mode = CompareMode.getInstance(ops[0], Integer.parseInt(ops[1]));
boolean binaryUnsigned = false;
if (options.length > 3) {
binaryUnsigned = Boolean.parseBoolean(options[3]);
}
CompareMode mode = CompareMode.getInstance(options[0], Integer.parseInt(options[1]), binaryUnsigned);
table.setCompareMode(mode);
meta = table.getScanIndex(session);
} else {
......@@ -1671,7 +1674,7 @@ public class PageStore implements CacheWriter {
cols[i] = ic;
}
IndexType indexType;
if (ops[3].equals("d")) {
if (options[3].equals("d")) {
indexType = IndexType.createPrimaryKey(true, false);
Column[] tableColumns = table.getColumns();
for (IndexColumn indexColumn : cols) {
......@@ -1743,6 +1746,7 @@ public class PageStore implements CacheWriter {
if (index instanceof PageDelegateIndex) {
options += "d";
}
options += "," + mode.isBinaryUnsigned();
Row row = metaTable.getTemplateRow();
row.setValue(0, ValueInt.get(index.getId()));
row.setValue(1, ValueInt.get(type));
......
......@@ -228,6 +228,33 @@ public class Utils {
return Integer.signum(data1.length - data2.length);
}
/**
* Compare the contents of two byte arrays. If the content or length of the
* first array is smaller than the second array, -1 is returned. If the
* content or length of the second array is smaller than the first array, 1
* is returned. If the contents and lengths are the same, 0 is returned.
* <p>
* This method interprets bytes as unsigned.
*
* @param data1 the first byte array (must not be null)
* @param data2 the second byte array (must not be null)
* @return the result of the comparison (-1, 1 or 0)
*/
public static int compareNotNullUnsigned(byte[] data1, byte[] data2) {
if (data1 == data2) {
return 0;
}
int len = Math.min(data1.length, data2.length);
for (int i = 0; i < len; i++) {
int b = data1[i] & 0xff;
int b2 = data2[i] & 0xff;
if (b != b2) {
return b > b2 ? 1 : -1;
}
}
return Integer.signum(data1.length - data2.length);
}
/**
* Compare the contents of two byte arrays. If the content or length of the
* first array is smaller than the second array, -1 is returned. If the
......
......@@ -34,6 +34,16 @@ public class CompareMode {
*/
public static final String ICU4J = "ICU4J_";
/**
* This constant means that the BINARY columns are sorted as if the bytes were signed.
*/
public static final String SIGNED = "SIGNED";
/**
* This constant means that the BINARY columns are sorted as if the bytes were unsigned.
*/
public static final String UNSIGNED = "UNSIGNED";
private static CompareMode lastUsed;
private static final boolean CAN_USE_ICU4J;
......@@ -51,10 +61,13 @@ public class CompareMode {
private final String name;
private final int strength;
/** if true, sort BINARY columns as if they contain unsigned bytes */
private final boolean binaryUnsigned;
protected CompareMode(String name, int strength) {
protected CompareMode(String name, int strength, boolean binaryUnsigned) {
this.name = name;
this.strength = strength;
this.binaryUnsigned = binaryUnsigned;
}
/**
......@@ -67,7 +80,7 @@ public class CompareMode {
* @param strength the collation strength
* @return the compare mode
*/
public static synchronized CompareMode getInstance(String name, int strength) {
public static synchronized CompareMode getInstance(String name, int strength, boolean binaryUnsigned) {
if (lastUsed != null) {
if (StringUtils.equals(lastUsed.name, name)) {
if (lastUsed.strength == strength) {
......@@ -76,7 +89,7 @@ public class CompareMode {
}
}
if (name == null || name.equals(OFF)) {
lastUsed = new CompareMode(name, strength);
lastUsed = new CompareMode(name, strength, binaryUnsigned);
} else {
boolean useICU4J;
if (name.startsWith(ICU4J)) {
......@@ -89,9 +102,9 @@ public class CompareMode {
useICU4J = CAN_USE_ICU4J;
}
if (useICU4J) {
lastUsed = new CompareModeIcu4J(name, strength);
lastUsed = new CompareModeIcu4J(name, strength, binaryUnsigned);
} else {
lastUsed = new CompareModeDefault(name, strength);
lastUsed = new CompareModeDefault(name, strength, binaryUnsigned);
}
}
return lastUsed;
......@@ -207,5 +220,9 @@ public class CompareMode {
public int getStrength() {
return strength;
}
public boolean isBinaryUnsigned() {
return binaryUnsigned;
}
}
......@@ -20,8 +20,8 @@ public class CompareModeDefault extends CompareMode {
private final Collator collator;
private final SmallLRUCache<String, CollationKey> collationKeys;
protected CompareModeDefault(String name, int strength) {
super(name, strength);
protected CompareModeDefault(String name, int strength, boolean binaryUnsigned) {
super(name, strength, binaryUnsigned);
collator = CompareMode.getCollator(name);
if (collator == null) {
throw DbException.throwInternalError(name);
......
......@@ -20,8 +20,8 @@ public class CompareModeIcu4J extends CompareMode {
private final Comparator<String> collator;
protected CompareModeIcu4J(String name, int strength) {
super(name, strength);
protected CompareModeIcu4J(String name, int strength, boolean binaryUnsigned) {
super(name, strength, binaryUnsigned);
collator = getIcu4jCollator(name, strength);
}
......
......@@ -87,7 +87,11 @@ public class ValueBytes extends Value {
protected int compareSecure(Value v, CompareMode mode) {
byte[] v2 = ((ValueBytes) v).value;
return Utils.compareNotNullSigned(value, v2);
if (mode.isBinaryUnsigned()) {
return Utils.compareNotNullUnsigned(value, v2);
} else {
return Utils.compareNotNullSigned(value, v2);
}
}
public String getString() {
......
......@@ -96,6 +96,7 @@ public class TestCases extends TestBase {
testDoubleRecovery();
testConstraintReconnect();
testCollation();
testBinaryCollation();
deleteDb("cases");
}
......@@ -830,6 +831,43 @@ public class TestCases extends TestBase {
conn.close();
}
private void testBinaryCollation() throws SQLException {
deleteDb("cases");
Connection conn = getConnection("cases");
Statement stat = conn.createStatement();
ResultSet rs;
// test the default (SIGNED)
stat.execute("create table bin( x binary(1) );");
stat.execute("insert into bin(x) values (x'09'),(x'0a'),(x'99'),(x'aa');");
rs = stat.executeQuery("select * from bin order by x;");
rs.next();
assertEquals("99", rs.getString(1));
rs.next();
assertEquals("aa", rs.getString(1));
rs.next();
assertEquals("09", rs.getString(1));
rs.next();
assertEquals("0a", rs.getString(1));
// test UNSIGNED mode
stat.execute("drop table bin");
stat.execute("SET BINARY_COLLATION UNSIGNED");
stat.execute("create table bin( x binary(1) );");
stat.execute("insert into bin(x) values (x'09'),(x'0a'),(x'99'),(x'aa');");
rs = stat.executeQuery("select * from bin order by x;");
rs.next();
assertEquals("09", rs.getString(1));
rs.next();
assertEquals("0a", rs.getString(1));
rs.next();
assertEquals("99", rs.getString(1));
rs.next();
assertEquals("aa", rs.getString(1));
conn.close();
}
private void testPersistentSettings() throws SQLException {
deleteDb("cases");
Connection conn = getConnection("cases");
......
......@@ -49,7 +49,7 @@ import org.h2.value.ValueUuid;
public class TestDataPage extends TestBase implements DataHandler {
private boolean testPerformance;
private final CompareMode compareMode = CompareMode.getInstance(null, 0);
private final CompareMode compareMode = CompareMode.getInstance(null, 0, false);
/**
* Run just this test.
......
......@@ -32,18 +32,18 @@ public class TestPattern extends TestBase {
private void testCompareModeReuse() {
CompareMode mode1, mode2;
mode1 = CompareMode.getInstance(null, 0);
mode2 = CompareMode.getInstance(null, 0);
mode1 = CompareMode.getInstance(null, 0, false);
mode2 = CompareMode.getInstance(null, 0, false);
assertTrue(mode1 == mode2);
mode1 = CompareMode.getInstance("DE", Collator.SECONDARY);
mode1 = CompareMode.getInstance("DE", Collator.SECONDARY, false);
assertFalse(mode1 == mode2);
mode2 = CompareMode.getInstance("DE", Collator.SECONDARY);
mode2 = CompareMode.getInstance("DE", Collator.SECONDARY, false);
assertTrue(mode1 == mode2);
}
private void testPattern() {
CompareMode mode = CompareMode.getInstance(null, 0);
CompareMode mode = CompareMode.getInstance(null, 0, false);
CompareLike comp = new CompareLike(mode, "\\", null, null, null, false);
test(comp, "B", "%_");
test(comp, "A", "A%");
......
......@@ -30,7 +30,7 @@ import org.h2.value.ValueInt;
*/
public class TestValueHashMap extends TestBase implements DataHandler {
CompareMode compareMode = CompareMode.getInstance(null, 0);
CompareMode compareMode = CompareMode.getInstance(null, 0, false);
/**
* Run just this test.
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论