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