comparison src/main/java/org/monetdb/jdbc/MonetConnection.java @ 905:a52bc2dcdb8c

Implement ClientInfo API And move ClientInfo out out MapiSocket
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Mon, 17 Jun 2024 15:54:45 +0200 (10 months ago)
parents 778959b2e0a4
children 8c8c423dc619
comparison
equal deleted inserted replaced
904:2d880f90be2a 905:a52bc2dcdb8c
19 import java.sql.CallableStatement; 19 import java.sql.CallableStatement;
20 import java.sql.Connection; 20 import java.sql.Connection;
21 import java.sql.DatabaseMetaData; 21 import java.sql.DatabaseMetaData;
22 import java.sql.PreparedStatement; 22 import java.sql.PreparedStatement;
23 import java.sql.ResultSet; 23 import java.sql.ResultSet;
24 import java.sql.ResultSetMetaData;
24 import java.sql.SQLClientInfoException; 25 import java.sql.SQLClientInfoException;
25 import java.sql.SQLException; 26 import java.sql.SQLException;
26 import java.sql.SQLFeatureNotSupportedException; 27 import java.sql.SQLFeatureNotSupportedException;
27 import java.sql.SQLNonTransientConnectionException; 28 import java.sql.SQLNonTransientConnectionException;
28 import java.sql.SQLWarning; 29 import java.sql.SQLWarning;
33 import java.util.concurrent.Executor; 34 import java.util.concurrent.Executor;
34 35
35 import org.monetdb.mcl.io.BufferedMCLReader; 36 import org.monetdb.mcl.io.BufferedMCLReader;
36 import org.monetdb.mcl.io.BufferedMCLWriter; 37 import org.monetdb.mcl.io.BufferedMCLWriter;
37 import org.monetdb.mcl.io.LineType; 38 import org.monetdb.mcl.io.LineType;
39 import org.monetdb.mcl.net.ClientInfo;
38 import org.monetdb.mcl.net.MapiSocket; 40 import org.monetdb.mcl.net.MapiSocket;
39 import org.monetdb.mcl.net.Target; 41 import org.monetdb.mcl.net.Target;
40 import org.monetdb.mcl.parser.HeaderLineParser; 42 import org.monetdb.mcl.parser.HeaderLineParser;
41 import org.monetdb.mcl.parser.MCLParseException; 43 import org.monetdb.mcl.parser.MCLParseException;
42 import org.monetdb.mcl.parser.StartOfHeaderParser; 44 import org.monetdb.mcl.parser.StartOfHeaderParser;
116 final String[] queryTempl = new String[3]; // pre, post, sep 118 final String[] queryTempl = new String[3]; // pre, post, sep
117 119
118 /** A template to apply to each command (like pre and post fixes), filled in constructor */ 120 /** A template to apply to each command (like pre and post fixes), filled in constructor */
119 private final String[] commandTempl = new String[2]; // pre, post 121 private final String[] commandTempl = new String[2]; // pre, post
120 122
123 /** A mapping of ClientInfo property names such as 'ClientHostname' to columns of the
124 * sessions table, such as 'hostname'.
125 */
126 private HashMap<String,String> clientInfoAttributeNames = null;
127
121 /** the SQL language */ 128 /** the SQL language */
122 private static final int LANG_SQL = 0; 129 private static final int LANG_SQL = 0;
123 /** the MAL language (officially *NOT* supported) */ 130 /** the MAL language (officially *NOT* supported) */
124 private static final int LANG_MAL = 3; 131 private static final int LANG_MAL = 3;
125 /** an unknown language */ 132 /** an unknown language */
222 sqle.setNextException(new SQLNonTransientConnectionException(connex[1], "08001")); 229 sqle.setNextException(new SQLNonTransientConnectionException(connex[1], "08001"));
223 } 230 }
224 throw sqle; 231 throw sqle;
225 } 232 }
226 233
227 // send any clientinfo 234 if (server.canClientInfo() && target.sendClientInfo()) {
228 if (server.hasClientInfo()) { 235 ClientInfo info = new ClientInfo();
229 sendControlCommand("clientinfo " + server.getClientInfo().format()); 236 info.setDefaults();
237 String clientApplication = target.getClientApplication();
238 String clientRemark = target.getClientRemark();
239 if (!clientApplication.isEmpty())
240 info.set("ApplicationName", clientApplication);
241 if (!clientRemark.isEmpty())
242 info.set("ClientRemark", clientRemark);
243 sendClientInfo(info);
230 } 244 }
231 245
232 // Now take care of any options not handled during the handshake 246 // Now take care of any options not handled during the handshake
233 curReplySize = defaultFetchSize; 247 curReplySize = defaultFetchSize;
234 if (lang == LANG_SQL) { 248 if (lang == LANG_SQL) {
1268 * or this method is called on a closed connection 1282 * or this method is called on a closed connection
1269 * @since 1.6 1283 * @since 1.6
1270 */ 1284 */
1271 @Override 1285 @Override
1272 public String getClientInfo(final String name) throws SQLException { 1286 public String getClientInfo(final String name) throws SQLException {
1273 // MonetDB doesn't support any Client Info Properties yet 1287 String attrName = getClientInfoAttributeNames().get(name);
1274 return null; 1288 if (attrName == null)
1289 return null;
1290 String query = "SELECT " + attrName + " FROM sys.sessions WHERE sessionid = current_sessionid()";
1291 try (Statement st = createStatement(); ResultSet rs = st.executeQuery(query)) {
1292 if (rs.next())
1293 return rs.getString(1);
1294 else
1295 return null;
1296 }
1275 } 1297 }
1276 1298
1277 /** 1299 /**
1278 * Returns a list containing the name and current value of each client info 1300 * Returns a list containing the name and current value of each client info
1279 * property supported by the driver. The value of a client info property may 1301 * property supported by the driver. The value of a client info property may
1287 * @since 1.6 1309 * @since 1.6
1288 */ 1310 */
1289 @Override 1311 @Override
1290 public Properties getClientInfo() throws SQLException { 1312 public Properties getClientInfo() throws SQLException {
1291 // MonetDB doesn't support any Client Info Properties yet 1313 // MonetDB doesn't support any Client Info Properties yet
1292 return new Properties(); 1314 Properties props = new Properties();
1315
1316 if (server.canClientInfo()) {
1317 StringBuilder builder = new StringBuilder("SELECT ");
1318 String sep = "";
1319 for (Entry<String, String> entry: getClientInfoAttributeNames().entrySet()) {
1320 String jdbcName = entry.getKey();
1321 String attrName = entry.getValue();
1322 builder.append(sep);
1323 sep = ", ";
1324 builder.append(attrName);
1325 builder.append(" AS \"");
1326 builder.append(jdbcName);
1327 builder.append("\"");
1328 }
1329 builder.append(" FROM sys.sessions WHERE sessionid = current_sessionid()");
1330
1331 try (
1332 Statement st = createStatement();
1333 ResultSet rs = st.executeQuery(builder.toString())
1334 ) {
1335 if (rs.next()) {
1336 ResultSetMetaData md = rs.getMetaData();
1337 for (int i = 1; i <= md.getColumnCount(); i++) {
1338 String key = md.getColumnName(i);
1339 String value = rs.getString(i);
1340 props.setProperty(key, value != null ? value : "");
1341 }
1342 }
1343 }
1344 }
1345 return props;
1346 }
1347
1348 private HashMap<String,String> getClientInfoAttributeNames() throws SQLException {
1349 if (clientInfoAttributeNames == null) {
1350 HashMap<String, String> map = new HashMap<>();
1351 try (Statement st = createStatement(); ResultSet rs = st.executeQuery("SELECT prop, session_attr FROM sys.clientinfo_properties")) {
1352 while (rs.next()) {
1353 String jdbcName = rs.getString(1);
1354 String attrName = rs.getString(2);
1355 map.put(jdbcName, attrName);
1356 }
1357 }
1358 clientInfoAttributeNames = map;
1359 }
1360 return clientInfoAttributeNames;
1293 } 1361 }
1294 1362
1295 /** 1363 /**
1296 * Sets the value of the client info property specified by name to the value specified by value. 1364 * Sets the value of the client info property specified by name to the value specified by value.
1297 * Applications may use the DatabaseMetaData.getClientInfoProperties method to determine 1365 * Applications may use the DatabaseMetaData.getClientInfoProperties method to determine
1328 * or this method is called on a closed connection 1396 * or this method is called on a closed connection
1329 * @since 1.6 1397 * @since 1.6
1330 */ 1398 */
1331 @Override 1399 @Override
1332 public void setClientInfo(final String name, final String value) throws SQLClientInfoException { 1400 public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
1333 // MonetDB doesn't support any Client Info Properties yet 1401 ClientInfo info = new ClientInfo();
1334 addWarning("setClientInfo: client info property name not recognized", "01M07"); 1402 try {
1403 info.set(name, value, getClientInfoAttributeNames().keySet());
1404 sendClientInfo(info);
1405 SQLWarning warn = info.warnings();
1406 if (warn != null)
1407 addWarning(warn);
1408 } catch (SQLException e) {
1409 throw info.wrapException(e);
1410 }
1335 } 1411 }
1336 1412
1337 /** 1413 /**
1338 * Sets the value of the connection's client info properties. 1414 * Sets the value of the connection's client info properties.
1339 * The Properties object contains the names and values of the client info 1415 * The Properties object contains the names and values of the client info
1357 * or this method is called on a closed connection 1433 * or this method is called on a closed connection
1358 * @since 1.6 1434 * @since 1.6
1359 */ 1435 */
1360 @Override 1436 @Override
1361 public void setClientInfo(final Properties props) throws SQLClientInfoException { 1437 public void setClientInfo(final Properties props) throws SQLClientInfoException {
1362 if (props != null) { 1438 ClientInfo info = new ClientInfo();
1363 for (Entry<Object, Object> entry : props.entrySet()) { 1439 try {
1364 setClientInfo(entry.getKey().toString(), entry.getValue().toString()); 1440 for (String name: props.stringPropertyNames()) {
1365 } 1441 String value = props.getProperty(name);
1366 } 1442 info.set(name, value, getClientInfoAttributeNames().keySet());
1443 }
1444 sendClientInfo(info);
1445 SQLWarning warn = info.warnings();
1446 if (warn != null)
1447 addWarning(warn);
1448 } catch (SQLClientInfoException e) {
1449 throw e;
1450 } catch (SQLException e) {
1451 throw info.wrapException(e);
1452 }
1453 }
1454
1455 private void sendClientInfo(ClientInfo info) throws SQLException {
1456 String formatted = info.format();
1457 if (!formatted.isEmpty())
1458 sendControlCommand("clientinfo " + formatted);
1367 } 1459 }
1368 1460
1369 //== Java 1.7 methods (JDBC 4.1) 1461 //== Java 1.7 methods (JDBC 4.1)
1370 1462
1371 /** 1463 /**
2050 * Adds a warning to the pile of warnings this Connection object has. 2142 * Adds a warning to the pile of warnings this Connection object has.
2051 * If there were no warnings (or clearWarnings was called) this 2143 * If there were no warnings (or clearWarnings was called) this
2052 * warning will be the first, otherwise this warning will get 2144 * warning will be the first, otherwise this warning will get
2053 * appended to the current warning. 2145 * appended to the current warning.
2054 * 2146 *
2147 * @param warning The warning to add
2148 */
2149 private final void addWarning(SQLWarning warning) {
2150 if (warnings == null) {
2151 warnings = warning;
2152 } else {
2153 warnings.setNextWarning(warning);
2154 }
2155 }
2156
2157 /**
2158 * Adds a warning to the pile of warnings this Connection object has.
2159 * If there were no warnings (or clearWarnings was called) this
2160 * warning will be the first, otherwise this warning will get
2161 * appended to the current warning.
2162 *
2055 * @param reason the warning message 2163 * @param reason the warning message
2056 * @param sqlstate the SQLState code (5 characters) 2164 * @param sqlstate the SQLState code (5 characters)
2057 */ 2165 */
2058 private final void addWarning(final String reason, final String sqlstate) { 2166 private final void addWarning(final String reason, final String sqlstate) {
2059 final SQLWarning warng = new SQLWarning(reason, sqlstate); 2167 final SQLWarning warning = new SQLWarning(reason, sqlstate);
2060 if (warnings == null) { 2168 addWarning(warning);
2061 warnings = warng;
2062 } else {
2063 warnings.setNextWarning(warng);
2064 }
2065 } 2169 }
2066 2170
2067 /** the default number of rows that are (attempted to) read at once */ 2171 /** the default number of rows that are (attempted to) read at once */
2068 private static final int DEF_FETCHSIZE = 250; 2172 private static final int DEF_FETCHSIZE = 250;
2069 2173