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

Merge pull request #1199 from katzyn/compatibility

Use MySQL-compatible conversion between CHAR and BINARY in MySQL mode
......@@ -14,6 +14,7 @@ import org.h2.command.Command;
import org.h2.command.CommandInterface;
import org.h2.command.Prepared;
import org.h2.engine.GeneratedKeys;
import org.h2.engine.Mode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
......@@ -151,6 +152,7 @@ public class Insert extends Prepared implements ResultTarget {
generatedKeys.initialize(table);
int listSize = list.size();
if (listSize > 0) {
Mode mode = session.getDatabase().getMode();
int columnLen = columns.length;
for (int x = 0; x < listSize; x++) {
session.startStatementWithinTransaction();
......@@ -166,7 +168,7 @@ public class Insert extends Prepared implements ResultTarget {
// e can be null (DEFAULT)
e = e.optimize(session);
try {
Value v = c.convert(e.getValue(session), session.getDatabase().getMode());
Value v = c.convert(e.getValue(session), mode);
newRow.setValue(index, v);
if (e instanceof SequenceValue) {
generatedKeys.add(c);
......@@ -239,11 +241,12 @@ public class Insert extends Prepared implements ResultTarget {
private Row addRowImpl(Value[] values) {
Row newRow = table.getTemplateRow();
setCurrentRowNumber(++rowNumber);
Mode mode = session.getDatabase().getMode();
for (int j = 0, len = columns.length; j < len; j++) {
Column c = columns[j];
int index = c.getColumnId();
try {
Value v = c.convert(values[j], session.getDatabase().getMode());
Value v = c.convert(values[j], mode);
newRow.setValue(index, v);
} catch (DbException ex) {
throw setRow(ex, rowNumber, getSQL(values));
......
......@@ -13,6 +13,7 @@ import org.h2.command.Command;
import org.h2.command.CommandInterface;
import org.h2.command.Prepared;
import org.h2.engine.GeneratedKeys;
import org.h2.engine.Mode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
......@@ -88,6 +89,7 @@ public class Merge extends Prepared {
session.getUser().checkRight(targetTable, Right.UPDATE);
setCurrentRowNumber(0);
GeneratedKeys generatedKeys = session.getGeneratedKeys();
Mode mode = session.getDatabase().getMode();
if (!valuesExpressionList.isEmpty()) {
// process values in list
count = 0;
......@@ -104,7 +106,7 @@ public class Merge extends Prepared {
if (e != null) {
// e can be null (DEFAULT)
try {
Value v = c.convert(e.getValue(session));
Value v = c.convert(e.getValue(session), mode);
newRow.setValue(index, v);
if (e instanceof SequenceValue) {
generatedKeys.add(c);
......@@ -134,7 +136,7 @@ public class Merge extends Prepared {
Column c = columns[j];
int index = c.getColumnId();
try {
Value v = c.convert(r[j]);
Value v = c.convert(r[j], mode);
newRow.setValue(index, v);
} catch (DbException ex) {
throw setRow(ex, count, getSQL(r));
......
......@@ -12,6 +12,7 @@ import org.h2.api.Trigger;
import org.h2.command.Command;
import org.h2.command.CommandInterface;
import org.h2.command.Prepared;
import org.h2.engine.Mode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
......@@ -82,6 +83,7 @@ public class Replace extends Prepared {
session.getUser().checkRight(table, Right.INSERT);
session.getUser().checkRight(table, Right.UPDATE);
setCurrentRowNumber(0);
Mode mode = session.getDatabase().getMode();
if (!list.isEmpty()) {
count = 0;
for (int x = 0, size = list.size(); x < size; x++) {
......@@ -95,7 +97,7 @@ public class Replace extends Prepared {
if (e != null) {
// e can be null (DEFAULT)
try {
Value v = c.convert(e.getValue(session));
Value v = c.convert(e.getValue(session), mode);
newRow.setValue(index, v);
} catch (DbException ex) {
throw setRow(ex, count, getSQL(expr));
......@@ -119,7 +121,7 @@ public class Replace extends Prepared {
Column c = columns[j];
int index = c.getColumnId();
try {
Value v = c.convert(r[j]);
Value v = c.convert(r[j], mode);
newRow.setValue(index, v);
} catch (DbException ex) {
throw setRow(ex, count, getSQL(r));
......
......@@ -131,7 +131,7 @@ public class Update extends Prepared {
} else if (newExpr == ValueExpression.getDefault()) {
newValue = table.getDefaultValue(session, column);
} else {
newValue = column.convert(newExpr.getValue(session));
newValue = column.convert(newExpr.getValue(session), session.getDatabase().getMode());
}
newRow.setValue(i, newValue);
}
......
......@@ -186,6 +186,11 @@ public class Mode {
*/
public boolean allowDB2TimestampFormat;
/**
* Convert (VAR)CHAR to VAR(BINARY) and vice versa with UTF-8 encoding instead of HEX.
*/
public boolean charToBinaryInUtf8;
/**
* An optional Set of hidden/disallowed column types.
* Certain DBMSs don't support all column types provided by H2, such as
......@@ -269,6 +274,7 @@ public class Mode {
mode.supportedClientInfoPropertiesRegEx =
Pattern.compile(".*");
mode.prohibitEmptyInPredicate = true;
mode.charToBinaryInUtf8 = true;
add(mode);
mode = new Mode(ModeEnum.Oracle);
......
......@@ -93,7 +93,7 @@ public class JdbcConnection extends TraceObject
private int queryTimeoutCache = -1;
private Map<String, String> clientInfo;
private String mode;
private volatile Mode mode;
private final boolean scopeGeneratedKeys;
/**
......@@ -1783,8 +1783,7 @@ public class JdbcConnection extends TraceObject
Collections.<String, ClientInfoStatus> emptyMap());
}
Pattern clientInfoNameRegEx = Mode
.getInstance(getMode()).supportedClientInfoPropertiesRegEx;
Pattern clientInfoNameRegEx = getMode().supportedClientInfoPropertiesRegEx;
if (clientInfoNameRegEx != null
&& clientInfoNameRegEx.matcher(name).matches()) {
......@@ -2102,15 +2101,22 @@ public class JdbcConnection extends TraceObject
trace.setLevel(level);
}
String getMode() throws SQLException {
Mode getMode() throws SQLException {
Mode mode = this.mode;
if (mode == null) {
PreparedStatement prep = prepareStatement(
"SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=?");
String name;
try (PreparedStatement prep = prepareStatement(
"SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=?")) {
prep.setString(1, "MODE");
ResultSet rs = prep.executeQuery();
rs.next();
mode = rs.getString(1);
prep.close();
name = rs.getString(1);
}
mode = Mode.getInstance(name);
if (mode == null) {
mode = Mode.getRegular();
}
this.mode = mode;
}
return mode;
}
......
......@@ -16,6 +16,7 @@ import java.util.Arrays;
import java.util.Properties;
import org.h2.engine.Constants;
import org.h2.engine.Mode.ModeEnum;
import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote;
import org.h2.engine.SysProperties;
......@@ -2562,8 +2563,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
@Override
public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException {
debugCodeCall("supportsMixedCaseQuotedIdentifiers");
String m = conn.getMode();
return !m.equals("MySQL");
return conn.getMode().getEnum() != ModeEnum.MySQL;
}
/**
......@@ -2575,8 +2575,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
@Override
public boolean storesUpperCaseIdentifiers() throws SQLException {
debugCodeCall("storesUpperCaseIdentifiers");
String m = conn.getMode();
return !m.equals("MySQL");
return conn.getMode().getEnum() != ModeEnum.MySQL;
}
/**
......@@ -2588,8 +2587,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
@Override
public boolean storesLowerCaseIdentifiers() throws SQLException {
debugCodeCall("storesLowerCaseIdentifiers");
String m = conn.getMode();
return m.equals("MySQL");
return conn.getMode().getEnum() == ModeEnum.MySQL;
}
/**
......@@ -2613,8 +2611,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
@Override
public boolean storesUpperCaseQuotedIdentifiers() throws SQLException {
debugCodeCall("storesUpperCaseQuotedIdentifiers");
String m = conn.getMode();
return m.equals("MySQL");
return conn.getMode().getEnum() == ModeEnum.MySQL;
}
/**
......@@ -2626,8 +2623,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
@Override
public boolean storesLowerCaseQuotedIdentifiers() throws SQLException {
debugCodeCall("storesLowerCaseQuotedIdentifiers");
String m = conn.getMode();
return m.equals("MySQL");
return conn.getMode().getEnum() == ModeEnum.MySQL;
}
/**
......@@ -2639,8 +2635,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
@Override
public boolean storesMixedCaseQuotedIdentifiers() throws SQLException {
debugCodeCall("storesMixedCaseQuotedIdentifiers");
String m = conn.getMode();
return !m.equals("MySQL");
return conn.getMode().getEnum() != ModeEnum.MySQL;
}
/**
......
......@@ -1065,7 +1065,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
public byte[] getBytes(int columnIndex) throws SQLException {
try {
debugCodeCall("getBytes", columnIndex);
return get(columnIndex).getBytes();
return get(columnIndex).convertTo(Value.BYTES, -1, conn.getMode()).getBytes();
} catch (Exception e) {
throw logAndConvert(e);
}
......
......@@ -957,6 +957,33 @@ public abstract class Value {
}
break;
}
case STRING: {
String s;
if (getType() == BYTES && mode != null && mode.charToBinaryInUtf8) {
s = new String(getBytesNoCopy());
} else {
s = getString();
}
return ValueString.get(s);
}
case STRING_IGNORECASE: {
String s;
if (getType() == BYTES && mode != null && mode.charToBinaryInUtf8) {
s = new String(getBytesNoCopy());
} else {
s = getString();
}
return ValueStringIgnoreCase.get(s);
}
case STRING_FIXED: {
String s;
if (getType() == BYTES && mode != null && mode.charToBinaryInUtf8) {
s = new String(getBytesNoCopy());
} else {
s = getString();
}
return ValueStringFixed.get(s, precision, mode);
}
case JAVA_OBJECT: {
switch (getType()) {
case BYTES:
......@@ -1081,17 +1108,11 @@ public abstract class Value {
case TIMESTAMP_TZ:
return ValueTimestampTimeZone.parse(s.trim());
case BYTES:
return ValueBytes.getNoCopy(
StringUtils.convertHexToBytes(s.trim()));
return ValueBytes.getNoCopy(mode != null && mode.charToBinaryInUtf8 ?
s.getBytes(StandardCharsets.UTF_8): StringUtils.convertHexToBytes(s.trim()));
case JAVA_OBJECT:
return ValueJavaObject.getNoCopy(null,
StringUtils.convertHexToBytes(s.trim()), getDataHandler());
case STRING:
return ValueString.get(s);
case STRING_IGNORECASE:
return ValueStringIgnoreCase.get(s);
case STRING_FIXED:
return ValueStringFixed.get(s, precision, mode);
case DOUBLE:
return ValueDouble.get(Double.parseDouble(s.trim()));
case FLOAT:
......
......@@ -5,6 +5,7 @@
*/
package org.h2.test.db;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
......@@ -304,6 +305,28 @@ public class TestCompatibility extends TestBase {
// check the weird MySQL variant of DELETE
stat.execute("DELETE TEST FROM TEST WHERE 1=2");
// Check conversion between VARCHAR and VARBINARY
String string = "ABCD\u1234";
byte[] bytes = string.getBytes(StandardCharsets.UTF_8);
stat.execute("CREATE TABLE TEST2(C VARCHAR, B VARBINARY)");
stat.execute("INSERT INTO TEST2(C) VALUES ('" + string + "')");
assertEquals(1, stat.executeUpdate("UPDATE TEST2 SET B = C"));
ResultSet rs = stat.executeQuery("SELECT B FROM TEST2");
assertTrue(rs.next());
assertEquals(bytes, rs.getBytes(1));
assertEquals(1, stat.executeUpdate("UPDATE TEST2 SET C = B"));
testMySQLBytesCheck(stat, string, bytes);
PreparedStatement prep = conn.prepareStatement("UPDATE TEST2 SET C = ?");
prep.setBytes(1, bytes);
assertEquals(1, prep.executeUpdate());
testMySQLBytesCheck(stat, string, bytes);
stat.execute("DELETE FROM TEST2");
prep = conn.prepareStatement("INSERT INTO TEST2(C) VALUES (?)");
prep.setBytes(1, bytes);
assertEquals(1, prep.executeUpdate());
testMySQLBytesCheck(stat, string, bytes);
stat.execute("DROP TABLE TEST2");
if (config.memory) {
return;
}
......@@ -324,7 +347,7 @@ public class TestCompatibility extends TestBase {
stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
assertResult("test", stat, "SHOW TABLES");
ResultSet rs = stat.executeQuery("SELECT * FROM TEST");
rs = stat.executeQuery("SELECT * FROM TEST");
rs.next();
rs.updateString(2, "Hallo");
rs.updateRow();
......@@ -391,6 +414,14 @@ public class TestCompatibility extends TestBase {
conn = getConnection("compatibility");
}
private void testMySQLBytesCheck(Statement stat, String string, byte[] bytes) throws SQLException {
ResultSet rs;
rs = stat.executeQuery("SELECT C FROM TEST2");
assertTrue(rs.next());
assertEquals(string, rs.getString(1));
assertEquals(bytes, rs.getBytes(1));
}
private void testSybaseAndMSSQLServer() throws SQLException {
Statement stat = conn.createStatement();
stat.execute("SET MODE MSSQLServer");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论