Changeset: a52bc2dcdb8c for monetdb-java URL: https://dev.monetdb.org/hg/monetdb-java/rev/a52bc2dcdb8c Modified Files: src/main/java/org/monetdb/jdbc/MonetConnection.java src/main/java/org/monetdb/mcl/net/ClientInfo.java src/main/java/org/monetdb/mcl/net/MapiSocket.java src/main/java/org/monetdb/mcl/net/Target.java Branch: default Log Message:
Implement ClientInfo API And move ClientInfo out out MapiSocket diffs (truncated from 390 to 300 lines): diff --git a/src/main/java/org/monetdb/jdbc/MonetConnection.java b/src/main/java/org/monetdb/jdbc/MonetConnection.java --- a/src/main/java/org/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/org/monetdb/jdbc/MonetConnection.java @@ -21,6 +21,7 @@ import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; @@ -35,6 +36,7 @@ import java.util.concurrent.Executor; import org.monetdb.mcl.io.BufferedMCLReader; import org.monetdb.mcl.io.BufferedMCLWriter; import org.monetdb.mcl.io.LineType; +import org.monetdb.mcl.net.ClientInfo; import org.monetdb.mcl.net.MapiSocket; import org.monetdb.mcl.net.Target; import org.monetdb.mcl.parser.HeaderLineParser; @@ -118,6 +120,11 @@ public class MonetConnection /** A template to apply to each command (like pre and post fixes), filled in constructor */ private final String[] commandTempl = new String[2]; // pre, post + /** A mapping of ClientInfo property names such as 'ClientHostname' to columns of the + * sessions table, such as 'hostname'. + */ + private HashMap<String,String> clientInfoAttributeNames = null; + /** the SQL language */ private static final int LANG_SQL = 0; /** the MAL language (officially *NOT* supported) */ @@ -224,9 +231,16 @@ public class MonetConnection throw sqle; } - // send any clientinfo - if (server.hasClientInfo()) { - sendControlCommand("clientinfo " + server.getClientInfo().format()); + if (server.canClientInfo() && target.sendClientInfo()) { + ClientInfo info = new ClientInfo(); + info.setDefaults(); + String clientApplication = target.getClientApplication(); + String clientRemark = target.getClientRemark(); + if (!clientApplication.isEmpty()) + info.set("ApplicationName", clientApplication); + if (!clientRemark.isEmpty()) + info.set("ClientRemark", clientRemark); + sendClientInfo(info); } // Now take care of any options not handled during the handshake @@ -1270,8 +1284,16 @@ public class MonetConnection */ @Override public String getClientInfo(final String name) throws SQLException { - // MonetDB doesn't support any Client Info Properties yet - return null; + String attrName = getClientInfoAttributeNames().get(name); + if (attrName == null) + return null; + String query = "SELECT " + attrName + " FROM sys.sessions WHERE sessionid = current_sessionid()"; + try (Statement st = createStatement(); ResultSet rs = st.executeQuery(query)) { + if (rs.next()) + return rs.getString(1); + else + return null; + } } /** @@ -1289,7 +1311,53 @@ public class MonetConnection @Override public Properties getClientInfo() throws SQLException { // MonetDB doesn't support any Client Info Properties yet - return new Properties(); + Properties props = new Properties(); + + if (server.canClientInfo()) { + StringBuilder builder = new StringBuilder("SELECT "); + String sep = ""; + for (Entry<String, String> entry: getClientInfoAttributeNames().entrySet()) { + String jdbcName = entry.getKey(); + String attrName = entry.getValue(); + builder.append(sep); + sep = ", "; + builder.append(attrName); + builder.append(" AS \""); + builder.append(jdbcName); + builder.append("\""); + } + builder.append(" FROM sys.sessions WHERE sessionid = current_sessionid()"); + + try ( + Statement st = createStatement(); + ResultSet rs = st.executeQuery(builder.toString()) + ) { + if (rs.next()) { + ResultSetMetaData md = rs.getMetaData(); + for (int i = 1; i <= md.getColumnCount(); i++) { + String key = md.getColumnName(i); + String value = rs.getString(i); + props.setProperty(key, value != null ? value : ""); + } + } + } + } + return props; + } + + private HashMap<String,String> getClientInfoAttributeNames() throws SQLException { + if (clientInfoAttributeNames == null) { + HashMap<String, String> map = new HashMap<>(); + try (Statement st = createStatement(); ResultSet rs = st.executeQuery("SELECT prop, session_attr FROM sys.clientinfo_properties")) { + while (rs.next()) { + String jdbcName = rs.getString(1); + String attrName = rs.getString(2); + map.put(jdbcName, attrName); + } + } + clientInfoAttributeNames = map; + } + return clientInfoAttributeNames; } /** @@ -1330,8 +1398,16 @@ public class MonetConnection */ @Override public void setClientInfo(final String name, final String value) throws SQLClientInfoException { - // MonetDB doesn't support any Client Info Properties yet - addWarning("setClientInfo: client info property name not recognized", "01M07"); + ClientInfo info = new ClientInfo(); + try { + info.set(name, value, getClientInfoAttributeNames().keySet()); + sendClientInfo(info); + SQLWarning warn = info.warnings(); + if (warn != null) + addWarning(warn); + } catch (SQLException e) { + throw info.wrapException(e); + } } /** @@ -1359,13 +1435,29 @@ public class MonetConnection */ @Override public void setClientInfo(final Properties props) throws SQLClientInfoException { - if (props != null) { - for (Entry<Object, Object> entry : props.entrySet()) { - setClientInfo(entry.getKey().toString(), entry.getValue().toString()); + ClientInfo info = new ClientInfo(); + try { + for (String name: props.stringPropertyNames()) { + String value = props.getProperty(name); + info.set(name, value, getClientInfoAttributeNames().keySet()); } + sendClientInfo(info); + SQLWarning warn = info.warnings(); + if (warn != null) + addWarning(warn); + } catch (SQLClientInfoException e) { + throw e; + } catch (SQLException e) { + throw info.wrapException(e); } } + private void sendClientInfo(ClientInfo info) throws SQLException { + String formatted = info.format(); + if (!formatted.isEmpty()) + sendControlCommand("clientinfo " + formatted); + } + //== Java 1.7 methods (JDBC 4.1) /** @@ -2052,16 +2144,28 @@ public class MonetConnection * warning will be the first, otherwise this warning will get * appended to the current warning. * + * @param warning The warning to add + */ + private final void addWarning(SQLWarning warning) { + if (warnings == null) { + warnings = warning; + } else { + warnings.setNextWarning(warning); + } + } + + /** + * Adds a warning to the pile of warnings this Connection object has. + * If there were no warnings (or clearWarnings was called) this + * warning will be the first, otherwise this warning will get + * appended to the current warning. + * * @param reason the warning message * @param sqlstate the SQLState code (5 characters) */ private final void addWarning(final String reason, final String sqlstate) { - final SQLWarning warng = new SQLWarning(reason, sqlstate); - if (warnings == null) { - warnings = warng; - } else { - warnings.setNextWarning(warng); - } + final SQLWarning warning = new SQLWarning(reason, sqlstate); + addWarning(warning); } /** the default number of rows that are (attempted to) read at once */ diff --git a/src/main/java/org/monetdb/mcl/net/ClientInfo.java b/src/main/java/org/monetdb/mcl/net/ClientInfo.java --- a/src/main/java/org/monetdb/mcl/net/ClientInfo.java +++ b/src/main/java/org/monetdb/mcl/net/ClientInfo.java @@ -9,10 +9,17 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.sql.ClientInfoStatus; import java.sql.SQLClientInfoException; -import java.util.Collections; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.Set; +/** + * Manage ClientInfo properties to track, and help generating a + * @{link SQLClientInfoException} if there is a failure + */ public class ClientInfo { private static final String defaultHostname = findHostname(); @@ -23,9 +30,13 @@ public class ClientInfo { private static final String defaultPid = findPid(); private final Properties props; + private HashMap<String, ClientInfoStatus> problems = null; public ClientInfo() { props = new Properties(); + } + + public void setDefaults() { props.setProperty("ClientHostname", defaultHostname); props.setProperty("ClientLibrary", defaultClientLibrary); props.setProperty("ClientPid", defaultPid); @@ -88,24 +99,56 @@ public class ClientInfo { } public Properties get() { - Properties ret = new Properties(); - ret.putAll(props); - return ret; + return props; } - public boolean set(String name, String value) throws SQLClientInfoException { + public HashMap<String,ClientInfoStatus> getProblems() { + return problems; + } + + public void set(String name, String value, Set<String> known) throws SQLClientInfoException { if (value == null) value = ""; - if (value.contains("\n")) { - Map<String, ClientInfoStatus> map = Collections.singletonMap(name, ClientInfoStatus.REASON_VALUE_INVALID); - throw new SQLClientInfoException(map); - } - if (props.containsKey(name)) { + + if (known != null && !known.contains(name)) { + addProblem(name, ClientInfoStatus.REASON_UNKNOWN_PROPERTY); + } else if (value.contains("\n")) { + addProblem(name, ClientInfoStatus.REASON_VALUE_INVALID); + throw new SQLClientInfoException("Invalid value for Client Info property '" + name + "'", "01M07", problems); + } else { props.setProperty(name, value); - return true; - } else { - return false; } } + public void set(String name, String value) throws SQLClientInfoException { + set(name, value, null); + } + + private void addProblem(String name, ClientInfoStatus status) { + if (problems == null) + problems = new HashMap<>(); + ClientInfoStatus old = problems.get(name); + if (old == null || status.compareTo(old) > 0) + problems.put(name, status); + } + + public SQLClientInfoException wrapException(SQLException e) { + return new SQLClientInfoException(problems, e); + } _______________________________________________ checkin-list mailing list -- checkin-list@monetdb.org To unsubscribe send an email to checkin-list-le...@monetdb.org