提交 5e7fe084 authored 作者: Niklas Mehner's avatar Niklas Mehner

Implement clientInfo. Depending on compatibility mode different properties are supported.

上级 38578625
...@@ -51,6 +51,9 @@ table with an LOB column. ...@@ -51,6 +51,9 @@ table with an LOB column.
<li>Issue #231: Possible infinite loop when initializing the ObjectDataType class <li>Issue #231: Possible infinite loop when initializing the ObjectDataType class
when concurrently writing into MVStore. when concurrently writing into MVStore.
</li> </li>
<ul>
<li>Added support for Connection.setClientInfo() in compatibility modes for DB2, Postgresql, Oracle and MySQL.
</li>
</ul> </ul>
<h2>Version 1.4.191 Beta (2016-01-21)</h2> <h2>Version 1.4.191 Beta (2016-01-21)</h2>
......
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
*/ */
package org.h2.engine; package org.h2.engine;
import java.util.HashMap;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import java.util.HashMap;
import java.util.regex.Pattern;
/** /**
* The compatibility modes. There is a fixed set of modes (for example * The compatibility modes. There is a fixed set of modes (for example
* PostgreSQL, MySQL). Each mode has different settings. * PostgreSQL, MySQL). Each mode has different settings.
...@@ -137,6 +139,11 @@ public class Mode { ...@@ -137,6 +139,11 @@ public class Mode {
*/ */
public boolean onDuplicateKeyUpdate; public boolean onDuplicateKeyUpdate;
/**
* Pattern describing the keys the java.sql.Connection.setClientInfo() method accepts.
*/
public Pattern supportedClientInfoPropertiesRegEx;
/** /**
* Support the # for column names * Support the # for column names
*/ */
...@@ -154,6 +161,9 @@ public class Mode { ...@@ -154,6 +161,9 @@ public class Mode {
mode.supportOffsetFetch = true; mode.supportOffsetFetch = true;
mode.sysDummy1 = true; mode.sysDummy1 = true;
mode.isolationLevelInSelectOrInsertStatement = true; mode.isolationLevelInSelectOrInsertStatement = true;
// See https://www.ibm.com/support/knowledgecenter/SSEPEK_11.0.0/com.ibm.db2z11.doc.java/src/tpc/imjcc_r0052001.dita
mode.supportedClientInfoPropertiesRegEx =
Pattern.compile("ApplicationName|ClientAccountingInformation|ClientUser|ClientCorrelationToken");
add(mode); add(mode);
mode = new Mode("Derby"); mode = new Mode("Derby");
...@@ -162,6 +172,8 @@ public class Mode { ...@@ -162,6 +172,8 @@ public class Mode {
mode.supportOffsetFetch = true; mode.supportOffsetFetch = true;
mode.sysDummy1 = true; mode.sysDummy1 = true;
mode.isolationLevelInSelectOrInsertStatement = true; mode.isolationLevelInSelectOrInsertStatement = true;
// Derby does not support client info properties as of version 10.12.1.1
mode.supportedClientInfoPropertiesRegEx = null;
add(mode); add(mode);
mode = new Mode("HSQLDB"); mode = new Mode("HSQLDB");
...@@ -170,6 +182,9 @@ public class Mode { ...@@ -170,6 +182,9 @@ public class Mode {
mode.nullConcatIsNull = true; mode.nullConcatIsNull = true;
mode.uniqueIndexSingleNull = true; mode.uniqueIndexSingleNull = true;
mode.allowPlusForStringConcat = true; mode.allowPlusForStringConcat = true;
// HSQLDB does not support client info properties. See
// http://hsqldb.org/doc/apidocs/org/hsqldb/jdbc/JDBCConnection.html#setClientInfo%28java.lang.String,%20java.lang.String%29
mode.supportedClientInfoPropertiesRegEx = null;
add(mode); add(mode);
mode = new Mode("MSSQLServer"); mode = new Mode("MSSQLServer");
...@@ -179,6 +194,9 @@ public class Mode { ...@@ -179,6 +194,9 @@ public class Mode {
mode.allowPlusForStringConcat = true; mode.allowPlusForStringConcat = true;
mode.swapConvertFunctionParameters = true; mode.swapConvertFunctionParameters = true;
mode.supportPoundSymbolForColumnNames = true; mode.supportPoundSymbolForColumnNames = true;
// MS SQL Server does not support client info properties. See
// https://msdn.microsoft.com/en-Us/library/dd571296%28v=sql.110%29.aspx
mode.supportedClientInfoPropertiesRegEx = null;
add(mode); add(mode);
mode = new Mode("MySQL"); mode = new Mode("MySQL");
...@@ -186,6 +204,9 @@ public class Mode { ...@@ -186,6 +204,9 @@ public class Mode {
mode.indexDefinitionInCreateTable = true; mode.indexDefinitionInCreateTable = true;
mode.lowerCaseIdentifiers = true; mode.lowerCaseIdentifiers = true;
mode.onDuplicateKeyUpdate = true; mode.onDuplicateKeyUpdate = true;
// MySQL allows to use any key for client info entries. See
// http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.24/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java
mode.supportedClientInfoPropertiesRegEx = Pattern.compile(".*");
add(mode); add(mode);
mode = new Mode("Oracle"); mode = new Mode("Oracle");
...@@ -194,6 +215,9 @@ public class Mode { ...@@ -194,6 +215,9 @@ public class Mode {
mode.uniqueIndexSingleNullExceptAllColumnsAreNull = true; mode.uniqueIndexSingleNullExceptAllColumnsAreNull = true;
mode.treatEmptyStringsAsNull = true; mode.treatEmptyStringsAsNull = true;
mode.supportPoundSymbolForColumnNames = true; mode.supportPoundSymbolForColumnNames = true;
// Oracle accepts keys of the form <namespace>.*. See
// https://docs.oracle.com/database/121/JJDBC/jdbcvers.htm#JJDBC29006
mode.supportedClientInfoPropertiesRegEx = Pattern.compile(".*\\..*");
add(mode); add(mode);
mode = new Mode("PostgreSQL"); mode = new Mode("PostgreSQL");
...@@ -203,6 +227,9 @@ public class Mode { ...@@ -203,6 +227,9 @@ public class Mode {
mode.systemColumns = true; mode.systemColumns = true;
mode.logIsLogBase10 = true; mode.logIsLogBase10 = true;
mode.serialColumnIsNotPK = true; mode.serialColumnIsNotPK = true;
// PostgreSQL only supports the ApplicationName property. See
// https://github.com/hhru/postgres-jdbc/blob/master/postgresql-jdbc-9.2-1002.src/org/postgresql/jdbc4/AbstractJdbc4Connection.java
mode.supportedClientInfoPropertiesRegEx = Pattern.compile("ApplicationName");
add(mode); add(mode);
} }
......
...@@ -9,32 +9,12 @@ import java.io.ByteArrayInputStream; ...@@ -9,32 +9,12 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.sql.Array; import java.sql.*;
import java.sql.Blob; import java.util.*;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo; import org.h2.engine.*;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote;
import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
...@@ -51,6 +31,8 @@ import org.h2.value.ValueString; ...@@ -51,6 +31,8 @@ import org.h2.value.ValueString;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
//*/ //*/
import java.util.regex.Pattern;
/** /**
* <p> * <p>
* Represents a connection (session) to a database. * Represents a connection (session) to a database.
...@@ -83,6 +65,8 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -83,6 +65,8 @@ public class JdbcConnection extends TraceObject implements Connection {
private final CloseWatcher watcher; private final CloseWatcher watcher;
private int queryTimeoutCache = -1; private int queryTimeoutCache = -1;
private Map<String, String> clientInfo;
/** /**
* INTERNAL * INTERNAL
*/ */
...@@ -116,6 +100,7 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -116,6 +100,7 @@ public class JdbcConnection extends TraceObject implements Connection {
this.url = ci.getURL(); this.url = ci.getURL();
closeOld(); closeOld();
watcher = CloseWatcher.register(this, session, keepOpenStackTrace); watcher = CloseWatcher.register(this, session, keepOpenStackTrace);
this.clientInfo = new HashMap<String, String> ();
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
} }
...@@ -139,6 +124,7 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -139,6 +124,7 @@ public class JdbcConnection extends TraceObject implements Connection {
this.getReadOnly = clone.getReadOnly; this.getReadOnly = clone.getReadOnly;
this.rollback = clone.rollback; this.rollback = clone.rollback;
this.watcher = null; this.watcher = null;
this.clientInfo = new HashMap<String, String> (clone.clientInfo);
} }
/** /**
...@@ -1661,10 +1647,20 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -1661,10 +1647,20 @@ public class JdbcConnection extends TraceObject implements Connection {
/** /**
* Set a client property. * Set a client property.
* This method always throws a SQLClientInfoException. * This method always throws a SQLClientInfoException in standard mode.
* In compatibility mode the following properties are supported:
* <p><ul>
* <li>DB2: The properties: ApplicationName, ClientAccountingInformation, ClientUser and ClientCorrelationToken
* are supported.
* <li>MySQL: All property names are supported.
* <li>Oracle: All properties in the form <namespace>.<key name> are supported.
* <li>PostgreSQL: The ApplicationName property is supported.
* </ul><p>
*
* For unsupported properties a SQLClientInfoException is thrown.
* *
* @param name the name of the property (ignored) * @param name the name of the property
* @param value the value (ignored) * @param value the value
*/ */
@Override @Override
public void setClientInfo(String name, String value) public void setClientInfo(String name, String value)
...@@ -1676,13 +1672,24 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -1676,13 +1672,24 @@ public class JdbcConnection extends TraceObject implements Connection {
+quote(value)+");"); +quote(value)+");");
} }
checkClosed(); checkClosed();
// we don't have any client properties, so just throw
throw new SQLClientInfoException(); Pattern clientInfoNameRegEx = getMode().supportedClientInfoPropertiesRegEx;
if (clientInfoNameRegEx != null && clientInfoNameRegEx.matcher(name).matches()) {
clientInfo.put(name, value);
} else {
throw new SQLClientInfoException("Client info name '" + name + "' not supported.",
Collections.<String, ClientInfoStatus> emptyMap());
}
} catch (Exception e) { } catch (Exception e) {
throw convertToClientInfoException(logAndConvert(e)); throw convertToClientInfoException(logAndConvert(e));
} }
} }
private Mode getMode() throws SQLException {
return Mode.getInstance(((JdbcDatabaseMetaData) getMetaData()).getMode());
}
private static SQLClientInfoException convertToClientInfoException( private static SQLClientInfoException convertToClientInfoException(
SQLException x) { SQLException x) {
if (x instanceof SQLClientInfoException) { if (x instanceof SQLClientInfoException) {
...@@ -1693,8 +1700,10 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -1693,8 +1700,10 @@ public class JdbcConnection extends TraceObject implements Connection {
} }
/** /**
* Set the client properties. * Set the client properties. This replaces all existing properties.
* This method always throws a SQLClientInfoException. *
* This method always throws a SQLClientInfoException in standard mode. In compatibility mode
* some properties may be supported (see setProperty(String, String) for details).
* *
* @param properties the properties (ignored) * @param properties the properties (ignored)
*/ */
...@@ -1705,8 +1714,10 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -1705,8 +1714,10 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCode("setClientInfo(properties);"); debugCode("setClientInfo(properties);");
} }
checkClosed(); checkClosed();
// we don't have any client properties, so just throw clientInfo.clear();
throw new SQLClientInfoException(); for (Map.Entry<Object, Object> entry : properties.entrySet()) {
setClientInfo((String) entry.getKey(), (String) entry.getValue());
}
} catch (Exception e) { } catch (Exception e) {
throw convertToClientInfoException(logAndConvert(e)); throw convertToClientInfoException(logAndConvert(e));
} }
...@@ -1731,6 +1742,11 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -1731,6 +1742,11 @@ public class JdbcConnection extends TraceObject implements Connection {
for (int i = 0; i < serverList.size(); i++) { for (int i = 0; i < serverList.size(); i++) {
p.setProperty("server" + String.valueOf(i), serverList.get(i)); p.setProperty("server" + String.valueOf(i), serverList.get(i));
} }
for (Map.Entry<String, String> entry : clientInfo.entrySet()) {
p.setProperty(entry.getKey(), entry.getValue());
}
return p; return p;
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
...@@ -1740,8 +1756,8 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -1740,8 +1756,8 @@ public class JdbcConnection extends TraceObject implements Connection {
/** /**
* Get a client property. * Get a client property.
* *
* @param name the client info name (ignored) * @param name the client info name
* @return the property value * @return the property value or null if the property is not found or not supported.
*/ */
@Override @Override
public String getClientInfo(String name) throws SQLException { public String getClientInfo(String name) throws SQLException {
...@@ -1750,12 +1766,7 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -1750,12 +1766,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeCall("getClientInfo", name); debugCodeCall("getClientInfo", name);
} }
checkClosed(); checkClosed();
Properties p = getClientInfo(); return getClientInfo().getProperty(name);
String s = p.getProperty(name);
if (s == null) {
throw new SQLClientInfoException();
}
return s;
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
} }
......
...@@ -11,6 +11,8 @@ import java.sql.PreparedStatement; ...@@ -11,6 +11,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.RowIdLifetime; import java.sql.RowIdLifetime;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Properties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -3077,6 +3079,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -3077,6 +3079,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
*/ */
@Override @Override
public ResultSet getClientInfoProperties() throws SQLException { public ResultSet getClientInfoProperties() throws SQLException {
Properties clientInfo = conn.getClientInfo();
// we don't have any client properties, so return an empty result set // we don't have any client properties, so return an empty result set
return new SimpleResultSet(); return new SimpleResultSet();
} }
...@@ -3163,7 +3166,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -3163,7 +3166,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
return getTraceObjectName() + ": " + conn; return getTraceObjectName() + ": " + conn;
} }
private String getMode() throws SQLException { String getMode() throws SQLException {
if (mode == null) { if (mode == null) {
PreparedStatement prep = conn.prepareStatement( PreparedStatement prep = conn.prepareStatement(
"SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=?"); "SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=?");
......
...@@ -86,6 +86,7 @@ import org.h2.test.jdbc.TestBatchUpdates; ...@@ -86,6 +86,7 @@ import org.h2.test.jdbc.TestBatchUpdates;
import org.h2.test.jdbc.TestCallableStatement; import org.h2.test.jdbc.TestCallableStatement;
import org.h2.test.jdbc.TestCancel; import org.h2.test.jdbc.TestCancel;
import org.h2.test.jdbc.TestConcurrentConnectionUsage; import org.h2.test.jdbc.TestConcurrentConnectionUsage;
import org.h2.test.jdbc.TestConnection;
import org.h2.test.jdbc.TestDatabaseEventListener; import org.h2.test.jdbc.TestDatabaseEventListener;
import org.h2.test.jdbc.TestDriver; import org.h2.test.jdbc.TestDriver;
import org.h2.test.jdbc.TestJavaObject; import org.h2.test.jdbc.TestJavaObject;
...@@ -182,6 +183,7 @@ import org.h2.test.unit.TestIntPerfectHash; ...@@ -182,6 +183,7 @@ import org.h2.test.unit.TestIntPerfectHash;
import org.h2.test.unit.TestJmx; import org.h2.test.unit.TestJmx;
import org.h2.test.unit.TestLocale; import org.h2.test.unit.TestLocale;
import org.h2.test.unit.TestMathUtils; import org.h2.test.unit.TestMathUtils;
import org.h2.test.unit.TestMode;
import org.h2.test.unit.TestModifyOnWrite; import org.h2.test.unit.TestModifyOnWrite;
import org.h2.test.unit.TestNetUtils; import org.h2.test.unit.TestNetUtils;
import org.h2.test.unit.TestObjectDeserialization; import org.h2.test.unit.TestObjectDeserialization;
...@@ -723,6 +725,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -723,6 +725,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestCallableStatement()); addTest(new TestCallableStatement());
addTest(new TestCancel()); addTest(new TestCancel());
addTest(new TestConcurrentConnectionUsage()); addTest(new TestConcurrentConnectionUsage());
addTest(new TestConnection());
addTest(new TestDatabaseEventListener()); addTest(new TestDatabaseEventListener());
addTest(new TestJavaObject()); addTest(new TestJavaObject());
addTest(new TestLimitUpdates()); addTest(new TestLimitUpdates());
...@@ -835,6 +838,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -835,6 +838,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestIntPerfectHash()); addTest(new TestIntPerfectHash());
addTest(new TestJmx()); addTest(new TestJmx());
addTest(new TestMathUtils()); addTest(new TestMathUtils());
addTest(new TestMode());
addTest(new TestModifyOnWrite()); addTest(new TestModifyOnWrite());
addTest(new TestOldVersion()); addTest(new TestOldVersion());
addTest(new TestObjectDeserialization()); addTest(new TestObjectDeserialization());
......
...@@ -1199,7 +1199,7 @@ public class TestMetaData extends TestBase { ...@@ -1199,7 +1199,7 @@ public class TestMetaData extends TestBase {
private void testClientInfo() throws SQLException { private void testClientInfo() throws SQLException {
Connection conn = getConnection("metaData"); Connection conn = getConnection("metaData");
assertThrows(SQLClientInfoException.class, conn).getClientInfo("xxx"); assertNull(conn.getClientInfo("xxx"));
DatabaseMetaData meta = conn.getMetaData(); DatabaseMetaData meta = conn.getMetaData();
ResultSet rs = meta.getClientInfoProperties(); ResultSet rs = meta.getClientInfoProperties();
assertFalse(rs.next()); assertFalse(rs.next());
......
...@@ -73,11 +73,6 @@ public class TestStatement extends TestBase { ...@@ -73,11 +73,6 @@ public class TestStatement extends TestBase {
map.put("x", Object.class); map.put("x", Object.class);
assertThrows(ErrorCode.FEATURE_NOT_SUPPORTED_1, conn). assertThrows(ErrorCode.FEATURE_NOT_SUPPORTED_1, conn).
setTypeMap(map); setTypeMap(map);
assertThrows(SQLClientInfoException.class, conn).
setClientInfo("X", "Y");
assertThrows(SQLClientInfoException.class, conn).
setClientInfo(new Properties());
} }
private void testTraceError() throws Exception { private void testTraceError() throws Exception {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论