comparison src/main/java/org/monetdb/jdbc/MonetConnection.java @ 671:fade6c6960cc

Refactor Handshake options It was not clear and there were obscure bugs.
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Fri, 28 Oct 2022 16:16:00 +0200 (2022-10-28)
parents 2448ce017593
children 8464a17caedf
comparison
equal deleted inserted replaced
670:3034312c1eda 671:fade6c6960cc
34 import java.util.concurrent.Executor; 34 import java.util.concurrent.Executor;
35 35
36 import org.monetdb.mcl.io.BufferedMCLReader; 36 import org.monetdb.mcl.io.BufferedMCLReader;
37 import org.monetdb.mcl.io.BufferedMCLWriter; 37 import org.monetdb.mcl.io.BufferedMCLWriter;
38 import org.monetdb.mcl.io.LineType; 38 import org.monetdb.mcl.io.LineType;
39 import org.monetdb.mcl.net.HandshakeOptions; 39 import org.monetdb.mcl.net.HandshakeOption;
40 import org.monetdb.mcl.net.HandshakeOptions.Setting;
41 import org.monetdb.mcl.net.MapiSocket; 40 import org.monetdb.mcl.net.MapiSocket;
42 import org.monetdb.mcl.parser.HeaderLineParser; 41 import org.monetdb.mcl.parser.HeaderLineParser;
43 import org.monetdb.mcl.parser.MCLParseException; 42 import org.monetdb.mcl.parser.MCLParseException;
44 import org.monetdb.mcl.parser.StartOfHeaderParser; 43 import org.monetdb.mcl.parser.StartOfHeaderParser;
45 44
164 * @throws IllegalArgumentException is one of the arguments is null or empty 163 * @throws IllegalArgumentException is one of the arguments is null or empty
165 */ 164 */
166 MonetConnection(final Properties props) 165 MonetConnection(final Properties props)
167 throws SQLException, IllegalArgumentException 166 throws SQLException, IllegalArgumentException
168 { 167 {
169 // for debug: System.out.println("New connection object. Received properties are: " + props.toString()); 168 HandshakeOption.AutoCommit autoCommitSetting = new HandshakeOption.AutoCommit(true);
169 HandshakeOption.ReplySize replySizeSetting = new HandshakeOption.ReplySize(DEF_FETCHSIZE);
170 HandshakeOption.SizeHeader sizeHeaderSetting = new HandshakeOption.SizeHeader(true);
171 HandshakeOption.TimeZone timeZoneSetting = new HandshakeOption.TimeZone(0);
172
173 // for debug: System.out.println("New connection object. Received properties are: " + props.toString());
170 // get supported property values from the props argument. 174 // get supported property values from the props argument.
171 // When a value is found add it to the internal conn_props list for use by getClientInfo(). 175 // When a value is found add it to the internal conn_props list for use by getClientInfo().
172 this.hostname = props.getProperty("host"); 176 this.hostname = props.getProperty("host");
173 if (this.hostname != null) 177 if (this.hostname != null)
174 conn_props.setProperty("host", this.hostname); 178 conn_props.setProperty("host", this.hostname);
209 final String hash = props.getProperty("hash"); 213 final String hash = props.getProperty("hash");
210 if (hash != null) 214 if (hash != null)
211 conn_props.setProperty("hash", hash); 215 conn_props.setProperty("hash", hash);
212 216
213 String autocommit_prop = props.getProperty("autocommit"); 217 String autocommit_prop = props.getProperty("autocommit");
214 boolean initial_autocommit = true;
215 if (autocommit_prop != null) { 218 if (autocommit_prop != null) {
216 initial_autocommit = Boolean.parseBoolean(autocommit_prop); 219 boolean ac = Boolean.parseBoolean(autocommit_prop);
217 conn_props.setProperty("autocommit", Boolean.toString(initial_autocommit)); 220 autoCommitSetting.set(ac);
221 conn_props.setProperty("autocommit", Boolean.toString(ac));
218 } 222 }
219 223
220 final String fetchsize_prop = props.getProperty("fetchsize"); 224 final String fetchsize_prop = props.getProperty("fetchsize");
221 if (fetchsize_prop != null) { 225 if (fetchsize_prop != null) {
222 try { 226 try {
223 int fetchsize = Integer.parseInt(fetchsize_prop); 227 int fetchsize = Integer.parseInt(fetchsize_prop);
224 if (fetchsize > 0 || fetchsize == -1) { 228 if (fetchsize > 0 || fetchsize == -1) {
225 this.defaultFetchSize = fetchsize; 229 replySizeSetting.set(fetchsize);
226 conn_props.setProperty("fetchsize", fetchsize_prop); 230 conn_props.setProperty("fetchsize", fetchsize_prop);
227 } else { 231 } else {
228 addWarning("Fetch size must either be positive or -1. Value " + fetchsize + " ignored", "M1M05"); 232 addWarning("Fetch size must either be positive or -1. Value " + fetchsize + " ignored", "M1M05");
229 } 233 }
230 } catch (NumberFormatException e) { 234 } catch (NumberFormatException e) {
289 server.setHash(hash); 293 server.setHash(hash);
290 if (database != null) 294 if (database != null)
291 server.setDatabase(database); 295 server.setDatabase(database);
292 server.setLanguage(language); 296 server.setLanguage(language);
293 297
298 // calculate our time zone offset
294 final Calendar cal = Calendar.getInstance(); 299 final Calendar cal = Calendar.getInstance();
295 int offsetMillis = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET); 300 int offsetMillis = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET);
296 int offsetSeconds = offsetMillis / 1000; 301 int offsetSeconds = offsetMillis / 1000;
297 final HandshakeOptions handshakeOptions = new HandshakeOptions(); 302 timeZoneSetting.set(offsetSeconds);
298 handshakeOptions.set(Setting.AutoCommit, initial_autocommit ? 1 : 0); 303
299 handshakeOptions.set(Setting.TimeZone, offsetSeconds); 304 server.setHandshakeOptions(new HandshakeOption[] {
300 handshakeOptions.set(Setting.ReplySize, defaultFetchSize); 305 autoCommitSetting,
301 // handshakeOptions.set(Setting.SizeHeader, 1); 306 replySizeSetting,
302 server.setHandshakeOptions(handshakeOptions); 307 sizeHeaderSetting,
303 autoCommit = initial_autocommit; 308 timeZoneSetting,
309 });
304 310
305 // we're debugging here... uhm, should be off in real life 311 // we're debugging here... uhm, should be off in real life
306 if (debug) { 312 if (debug) {
307 try { 313 try {
308 final String fname = props.getProperty("logfile", "monet_" + System.currentTimeMillis() + ".log"); 314 final String fname = props.getProperty("logfile", "monet_" + System.currentTimeMillis() + ".log");
379 //commandTempl[2] = ""; // separator (is not used) 385 //commandTempl[2] = ""; // separator (is not used)
380 } else { 386 } else {
381 lang = LANG_UNKNOWN; 387 lang = LANG_UNKNOWN;
382 } 388 }
383 389
384 // The reply size is checked before every query and adjusted if 390 // Now take care of any handshake options not handled during the handshake
385 // necessary. Update our current belief of what the server is set to. 391 if (replySizeSetting.isSent()) {
386 if (handshakeOptions.wasSentInHandshake(HandshakeOptions.Setting.ReplySize)) { 392 this.curReplySize = replySizeSetting.get();
387 this.curReplySize = handshakeOptions.get(HandshakeOptions.Setting.ReplySize); 393 }
388 } 394 this.defaultFetchSize = replySizeSetting.get();
389
390 for (Setting setting : new Setting[] { Setting.SizeHeader }) {
391 if (handshakeOptions.mustSend(setting)) {
392 Integer value = handshakeOptions.get(setting); // guaranteed by mustSend to be non-null
393 String command = String.format("%s %d", setting.getXCommand(), value);
394 sendControlCommand(command);
395 }
396 }
397
398 // the following initialisers are only valid when the language is SQL...
399 if (lang == LANG_SQL) { 395 if (lang == LANG_SQL) {
400 if (handshakeOptions.mustSend(Setting.AutoCommit)) { 396 if (autoCommitSetting.mustSend(autoCommit)) {
401 setAutoCommit(handshakeOptions.get(Setting.AutoCommit) != 0); 397 setAutoCommit(autoCommitSetting.get());
402 } 398 }
403 399 if (sizeHeaderSetting.mustSend(false)) {
404 // set our time zone on the server, if we haven't already 400 sendControlCommand("sizeheader 1");
405 if (handshakeOptions.mustSend(Setting.TimeZone)) { 401 }
406 final StringBuilder tz = new StringBuilder(64); 402 if (timeZoneSetting.mustSend(0)) {
407 tz.append("SET TIME ZONE INTERVAL '"); 403 setTimezone(timeZoneSetting.get());
408 int offsetMinutes = handshakeOptions.get(Setting.TimeZone) / 60; 404 }
409 if (offsetMinutes < 0) {
410 tz.append('-');
411 offsetMinutes = -offsetMinutes; // make it positive
412 } else {
413 tz.append('+');
414 }
415 int offsetHours = offsetMinutes / 60;
416 if (offsetHours < 10)
417 tz.append('0');
418 tz.append(offsetHours).append(':');
419 offsetMinutes -= offsetHours * 60;
420 if (offsetMinutes < 10)
421 tz.append('0');
422 tz.append(offsetMinutes).append("' HOUR TO MINUTE");
423 sendIndependentCommand(tz.toString());
424 }
425
426 // set sizeheader to 1 to enable sending "typesizes" info by the server (see mapi_set_size_header() in mapi.c)
427 sendControlCommand("sizeheader 1");
428 } 405 }
429 406
430 // we're absolutely not closed, since we're brand new 407 // we're absolutely not closed, since we're brand new
431 closed = false; 408 closed = false;
432 } 409 }
1735 /** 1712 /**
1736 * @return the currently registerered {@link DownloadHandler} handler, or null 1713 * @return the currently registerered {@link DownloadHandler} handler, or null
1737 */ 1714 */
1738 public DownloadHandler getDownloadHandler() { 1715 public DownloadHandler getDownloadHandler() {
1739 return downloadHandler; 1716 return downloadHandler;
1717 }
1718
1719 public void setTimezone(int offsetSeconds) throws SQLException {
1720 final StringBuilder tz = new StringBuilder(64);
1721 tz.append("SET TIME ZONE INTERVAL '");
1722 int offsetMinutes = offsetSeconds / 60;
1723 if (offsetMinutes < 0) {
1724 tz.append('-');
1725 offsetMinutes = -offsetMinutes; // make it positive
1726 } else {
1727 tz.append('+');
1728 }
1729 int offsetHours = offsetMinutes / 60;
1730 if (offsetHours < 10)
1731 tz.append('0');
1732 tz.append(offsetHours).append(':');
1733 offsetMinutes -= offsetHours * 60;
1734 if (offsetMinutes < 10)
1735 tz.append('0');
1736 tz.append(offsetMinutes).append("' HOUR TO MINUTE");
1737 sendIndependentCommand(tz.toString());
1740 } 1738 }
1741 1739
1742 /** 1740 /**
1743 * Local helper method to test whether the Connection object is closed 1741 * Local helper method to test whether the Connection object is closed
1744 * When closed it throws an SQLException 1742 * When closed it throws an SQLException