提交 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.
<li>Issue #231: Possible infinite loop when initializing the ObjectDataType class
when concurrently writing into MVStore.
</li>
<ul>
<li>Added support for Connection.setClientInfo() in compatibility modes for DB2, Postgresql, Oracle and MySQL.
</li>
</ul>
<h2>Version 1.4.191 Beta (2016-01-21)</h2>
......
......@@ -5,10 +5,12 @@
*/
package org.h2.engine;
import java.util.HashMap;
import org.h2.util.New;
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
* PostgreSQL, MySQL). Each mode has different settings.
......@@ -137,6 +139,11 @@ public class Mode {
*/
public boolean onDuplicateKeyUpdate;
/**
* Pattern describing the keys the java.sql.Connection.setClientInfo() method accepts.
*/
public Pattern supportedClientInfoPropertiesRegEx;
/**
* Support the # for column names
*/
......@@ -154,6 +161,9 @@ public class Mode {
mode.supportOffsetFetch = true;
mode.sysDummy1 = 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);
mode = new Mode("Derby");
......@@ -162,6 +172,8 @@ public class Mode {
mode.supportOffsetFetch = true;
mode.sysDummy1 = true;
mode.isolationLevelInSelectOrInsertStatement = true;
// Derby does not support client info properties as of version 10.12.1.1
mode.supportedClientInfoPropertiesRegEx = null;
add(mode);
mode = new Mode("HSQLDB");
......@@ -170,6 +182,9 @@ public class Mode {
mode.nullConcatIsNull = true;
mode.uniqueIndexSingleNull = 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);
mode = new Mode("MSSQLServer");
......@@ -179,6 +194,9 @@ public class Mode {
mode.allowPlusForStringConcat = true;
mode.swapConvertFunctionParameters = 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);
mode = new Mode("MySQL");
......@@ -186,6 +204,9 @@ public class Mode {
mode.indexDefinitionInCreateTable = true;
mode.lowerCaseIdentifiers = 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);
mode = new Mode("Oracle");
......@@ -194,6 +215,9 @@ public class Mode {
mode.uniqueIndexSingleNullExceptAllColumnsAreNull = true;
mode.treatEmptyStringsAsNull = 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);
mode = new Mode("PostgreSQL");
......@@ -203,6 +227,9 @@ public class Mode {
mode.systemColumns = true;
mode.logIsLogBase10 = 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);
}
......
......@@ -9,32 +9,12 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Array;
import java.sql.Blob;
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 java.sql.*;
import java.util.*;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote;
import org.h2.engine.SysProperties;
import org.h2.engine.*;
import org.h2.message.DbException;
import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
......@@ -51,6 +31,8 @@ import org.h2.value.ValueString;
import java.util.concurrent.Executor;
//*/
import java.util.regex.Pattern;
/**
* <p>
* Represents a connection (session) to a database.
......@@ -83,6 +65,8 @@ public class JdbcConnection extends TraceObject implements Connection {
private final CloseWatcher watcher;
private int queryTimeoutCache = -1;
private Map<String, String> clientInfo;
/**
* INTERNAL
*/
......@@ -116,6 +100,7 @@ public class JdbcConnection extends TraceObject implements Connection {
this.url = ci.getURL();
closeOld();
watcher = CloseWatcher.register(this, session, keepOpenStackTrace);
this.clientInfo = new HashMap<String, String> ();
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -139,6 +124,7 @@ public class JdbcConnection extends TraceObject implements Connection {
this.getReadOnly = clone.getReadOnly;
this.rollback = clone.rollback;
this.watcher = null;
this.clientInfo = new HashMap<String, String> (clone.clientInfo);
}
/**
......@@ -1661,10 +1647,20 @@ public class JdbcConnection extends TraceObject implements Connection {
/**
* 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 value the value (ignored)
* @param name the name of the property
* @param value the value
*/
@Override
public void setClientInfo(String name, String value)
......@@ -1676,13 +1672,24 @@ public class JdbcConnection extends TraceObject implements Connection {
+quote(value)+");");
}
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) {
throw convertToClientInfoException(logAndConvert(e));
}
}
private Mode getMode() throws SQLException {
return Mode.getInstance(((JdbcDatabaseMetaData) getMetaData()).getMode());
}
private static SQLClientInfoException convertToClientInfoException(
SQLException x) {
if (x instanceof SQLClientInfoException) {
......@@ -1693,8 +1700,10 @@ public class JdbcConnection extends TraceObject implements Connection {
}
/**
* Set the client properties.
* This method always throws a SQLClientInfoException.
* Set the client properties. This replaces all existing properties.
*
* 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)
*/
......@@ -1705,8 +1714,10 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCode("setClientInfo(properties);");
}
checkClosed();
// we don't have any client properties, so just throw
throw new SQLClientInfoException();
clientInfo.clear();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
setClientInfo((String) entry.getKey(), (String) entry.getValue());
}
} catch (Exception e) {
throw convertToClientInfoException(logAndConvert(e));
}
......@@ -1731,6 +1742,11 @@ public class JdbcConnection extends TraceObject implements Connection {
for (int i = 0; i < serverList.size(); 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;
} catch (Exception e) {
throw logAndConvert(e);
......@@ -1740,8 +1756,8 @@ public class JdbcConnection extends TraceObject implements Connection {
/**
* Get a client property.
*
* @param name the client info name (ignored)
* @return the property value
* @param name the client info name
* @return the property value or null if the property is not found or not supported.
*/
@Override
public String getClientInfo(String name) throws SQLException {
......@@ -1750,12 +1766,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeCall("getClientInfo", name);
}
checkClosed();
Properties p = getClientInfo();
String s = p.getProperty(name);
if (s == null) {
throw new SQLClientInfoException();
}
return s;
return getClientInfo().getProperty(name);
} catch (Exception e) {
throw logAndConvert(e);
}
......
......@@ -11,6 +11,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.RowIdLifetime;
import java.sql.SQLException;
import java.util.Properties;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
......@@ -3077,6 +3079,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
*/
@Override
public ResultSet getClientInfoProperties() throws SQLException {
Properties clientInfo = conn.getClientInfo();
// we don't have any client properties, so return an empty result set
return new SimpleResultSet();
}
......@@ -3163,7 +3166,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
return getTraceObjectName() + ": " + conn;
}
private String getMode() throws SQLException {
String getMode() throws SQLException {
if (mode == null) {
PreparedStatement prep = conn.prepareStatement(
"SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=?");
......
......@@ -86,6 +86,7 @@ import org.h2.test.jdbc.TestBatchUpdates;
import org.h2.test.jdbc.TestCallableStatement;
import org.h2.test.jdbc.TestCancel;
import org.h2.test.jdbc.TestConcurrentConnectionUsage;
import org.h2.test.jdbc.TestConnection;
import org.h2.test.jdbc.TestDatabaseEventListener;
import org.h2.test.jdbc.TestDriver;
import org.h2.test.jdbc.TestJavaObject;
......@@ -182,6 +183,7 @@ import org.h2.test.unit.TestIntPerfectHash;
import org.h2.test.unit.TestJmx;
import org.h2.test.unit.TestLocale;
import org.h2.test.unit.TestMathUtils;
import org.h2.test.unit.TestMode;
import org.h2.test.unit.TestModifyOnWrite;
import org.h2.test.unit.TestNetUtils;
import org.h2.test.unit.TestObjectDeserialization;
......@@ -723,6 +725,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestCallableStatement());
addTest(new TestCancel());
addTest(new TestConcurrentConnectionUsage());
addTest(new TestConnection());
addTest(new TestDatabaseEventListener());
addTest(new TestJavaObject());
addTest(new TestLimitUpdates());
......@@ -835,6 +838,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestIntPerfectHash());
addTest(new TestJmx());
addTest(new TestMathUtils());
addTest(new TestMode());
addTest(new TestModifyOnWrite());
addTest(new TestOldVersion());
addTest(new TestObjectDeserialization());
......
......@@ -1199,7 +1199,7 @@ public class TestMetaData extends TestBase {
private void testClientInfo() throws SQLException {
Connection conn = getConnection("metaData");
assertThrows(SQLClientInfoException.class, conn).getClientInfo("xxx");
assertNull(conn.getClientInfo("xxx"));
DatabaseMetaData meta = conn.getMetaData();
ResultSet rs = meta.getClientInfoProperties();
assertFalse(rs.next());
......
......@@ -73,11 +73,6 @@ public class TestStatement extends TestBase {
map.put("x", Object.class);
assertThrows(ErrorCode.FEATURE_NOT_SUPPORTED_1, conn).
setTypeMap(map);
assertThrows(SQLClientInfoException.class, conn).
setClientInfo("X", "Y");
assertThrows(SQLClientInfoException.class, conn).
setClientInfo(new Properties());
}
private void testTraceError() throws Exception {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论