Mercurial > hg > monetdb-java
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 |