Mercurial > hg > monetdb-java
diff src/main/java/org/monetdb/jdbc/MonetConnection.java @ 793:5bfe3357fb1c monetdbs
Use the new url parser
author | Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com> |
---|---|
date | Wed, 06 Dec 2023 16:17:13 +0100 (17 months ago) |
parents | 2f36ac68ac35 |
children | 895429110b7b |
line wrap: on
line diff
--- a/src/main/java/org/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/org/monetdb/jdbc/MonetConnection.java @@ -25,7 +25,6 @@ import java.sql.SQLWarning; import java.sql.Savepoint; import java.sql.Statement; import java.util.ArrayList; -import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -36,8 +35,8 @@ 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.HandshakeOption; import org.monetdb.mcl.net.MapiSocket; +import org.monetdb.mcl.net.Target; import org.monetdb.mcl.parser.HeaderLineParser; import org.monetdb.mcl.parser.MCLParseException; import org.monetdb.mcl.parser.StartOfHeaderParser; @@ -74,17 +73,8 @@ public class MonetConnection extends MonetWrapper implements Connection, AutoCloseable { - /** The hostname to connect to */ - private final String hostname; - /** The port to connect on the host to */ - private int port; - /** The database to use (currently not used) */ - private final String database; - /** The username to use when authenticating */ - private final String username; - /** The password to use when authenticating */ - private final String password; - + /** All connection parameters */ + Target target; /** A connection to mserver5 using a TCP socket */ private final MapiSocket server; /** The Reader from the server */ @@ -120,6 +110,9 @@ public class MonetConnection /** The number of results we receive from the server at once */ private int curReplySize = 100; // server default + private boolean sizeHeaderEnabled = false; // used during handshake + private boolean timeZoneSet = false; // used during handshake + /** A template to apply to each query (like pre and post fixes), filled in constructor */ // note: it is made public to the package as queryTempl[2] is used from MonetStatement @@ -137,11 +130,6 @@ public class MonetConnection /** The language which is used */ private final int lang; - /** Whether or not BLOB is mapped to Types.VARBINARY instead of Types.BLOB within this connection */ - private boolean treatBlobAsVarBinary = true; // turned on by default for optimal performance (from JDBC Driver release 3.0 onwards) - /** Whether or not CLOB is mapped to Types.VARCHAR instead of Types.CLOB within this connection */ - private boolean treatClobAsVarChar = true; // turned on by default for optimal performance (from JDBC Driver release 3.0 onwards) - /** The last set query timeout on the server as used by Statement, PreparedStatement and CallableStatement */ protected int lastSetQueryTimeout = 0; // 0 means no timeout, which is the default on the server @@ -155,137 +143,23 @@ public class MonetConnection * createStatement() call. This constructor is only accessible to * classes from the jdbc package. * - * @param props a Property hashtable holding the properties needed for connecting + * @param target a Target object holding the connection parameters * @throws SQLException if a database error occurs * @throws IllegalArgumentException is one of the arguments is null or empty */ - MonetConnection(final Properties props) + MonetConnection(Target target) throws SQLException, IllegalArgumentException { - HandshakeOption.AutoCommit autoCommitSetting = new HandshakeOption.AutoCommit(true); - HandshakeOption.ReplySize replySizeSetting = new HandshakeOption.ReplySize(DEF_FETCHSIZE); - HandshakeOption.SizeHeader sizeHeaderSetting = new HandshakeOption.SizeHeader(true); - HandshakeOption.TimeZone timeZoneSetting = new HandshakeOption.TimeZone(0); - - // for debug: System.out.println("New connection object. Received properties are: " + props.toString()); - // get supported property values from the props argument. - this.hostname = props.getProperty("host"); - - final String port_prop = props.getProperty("port"); - if (port_prop != null) { - try { - this.port = Integer.parseInt(port_prop); - } catch (NumberFormatException e) { - addWarning("Unable to parse port number from: " + port_prop, "M1M05"); - } - } - - this.database = props.getProperty("database"); - this.username = props.getProperty("user"); - this.password = props.getProperty("password"); - String language = props.getProperty("language"); - - boolean debug = false; - String debug_prop = props.getProperty("debug"); - if (debug_prop != null) { - debug = Boolean.parseBoolean(debug_prop); - } - - final String hash = props.getProperty("hash"); - - String autocommit_prop = props.getProperty("autocommit"); - if (autocommit_prop != null) { - boolean ac = Boolean.parseBoolean(autocommit_prop); - autoCommitSetting.set(ac); - } - - final String fetchsize_prop = props.getProperty("fetchsize"); - if (fetchsize_prop != null) { - try { - int fetchsize = Integer.parseInt(fetchsize_prop); - if (fetchsize > 0 || fetchsize == -1) { - replySizeSetting.set(fetchsize); - } else { - addWarning("Fetch size must either be positive or -1. Value " + fetchsize + " ignored", "M1M05"); - } - } catch (NumberFormatException e) { - addWarning("Unable to parse fetch size number from: " + fetchsize_prop, "M1M05"); - } - } - - final String treatBlobAsVarBinary_prop = props.getProperty("treat_blob_as_binary"); - if (treatBlobAsVarBinary_prop != null) { - treatBlobAsVarBinary = Boolean.parseBoolean(treatBlobAsVarBinary_prop); - if (treatBlobAsVarBinary) - typeMap.put("blob", Byte[].class); - } - - final String treatClobAsVarChar_prop = props.getProperty("treat_clob_as_varchar"); - if (treatClobAsVarChar_prop != null) { - treatClobAsVarChar = Boolean.parseBoolean(treatClobAsVarChar_prop); - if (treatClobAsVarChar) - typeMap.put("clob", String.class); - } - - int sockTimeout = 0; - final String so_timeout_prop = props.getProperty("so_timeout"); - if (so_timeout_prop != null) { - try { - sockTimeout = Integer.parseInt(so_timeout_prop); - if (sockTimeout < 0) { - addWarning("Negative socket timeout not allowed. Value ignored", "M1M05"); - sockTimeout = 0; - } - } catch (NumberFormatException e) { - addWarning("Unable to parse socket timeout number from: " + so_timeout_prop, "M1M05"); - } - } - - // check mandatory input arguments - if (hostname == null || hostname.isEmpty()) - throw new IllegalArgumentException("Missing or empty host name"); - if (port <= 0 || port > 65535) - throw new IllegalArgumentException("Invalid port number: " + port - + ". It should not be " + (port < 0 ? "negative" : (port > 65535 ? "larger than 65535" : "0"))); - if (username == null || username.isEmpty()) - throw new IllegalArgumentException("Missing or empty user name"); - if (password == null || password.isEmpty()) - throw new IllegalArgumentException("Missing or empty password"); - if (language == null || language.isEmpty()) { - // fallback to default language: sql - language = "sql"; - addWarning("No language specified, defaulting to 'sql'", "M1M05"); - } - - // warn about unrecognized property names - for (Entry<Object,Object> e: props.entrySet()) { - checkValidProperty(e.getKey().toString(), "MonetConnection"); - } - + this.target = target; server = new MapiSocket(); - if (hash != null) - server.setHash(hash); - if (database != null) - server.setDatabase(database); - server.setLanguage(language); - - // calculate our time zone offset - final Calendar cal = Calendar.getInstance(); - final int offsetMillis = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET); - final int offsetSeconds = offsetMillis / 1000; - timeZoneSetting.set(offsetSeconds); - - server.setHandshakeOptions(new HandshakeOption<?>[] { - autoCommitSetting, - replySizeSetting, - sizeHeaderSetting, - timeZoneSetting, - }); // we're debugging here... uhm, should be off in real life - if (debug) { + if (target.isDebug()) { try { - final String fname = props.getProperty("logfile", "monet_" + System.currentTimeMillis() + ".log"); + String fname = target.getLogfile(); + if (fname == null) + fname = "monet_" + System.currentTimeMillis() + ".log"; + File f = new File(fname); int ext = fname.lastIndexOf('.'); @@ -304,26 +178,45 @@ public class MonetConnection } } + SqlOptionsCallback callback = null; + switch (target.getLanguage()) { + case "sql": + lang = LANG_SQL; + queryTempl[0] = "s"; // pre + queryTempl[1] = "\n;"; // post + queryTempl[2] = "\n;\n"; // separator + commandTempl[0] = "X"; // pre + commandTempl[1] = ""; // post + callback = new SqlOptionsCallback(); + break; + case "mal": + lang = LANG_MAL; + queryTempl[0] = ""; // pre + queryTempl[1] = ";\n"; // post + queryTempl[2] = ";\n"; // separator + commandTempl[0] = ""; // pre + commandTempl[1] = ""; // post + break; + default: + lang = LANG_UNKNOWN; + break; + } + try { - final java.util.List<String> warnings = server.connect(hostname, port, username, password); + + final java.util.List<String> warnings = server.connect(target, callback); for (String warning : warnings) { addWarning(warning, "01M02"); } - // apply NetworkTimeout value from legacy (pre 4.1) driver - // so_timeout calls - server.setSoTimeout(sockTimeout); - in = server.getReader(); out = server.getWriter(); final String error = in.discardRemainder(); if (error != null) throw new SQLNonTransientConnectionException((error.length() > 6) ? error.substring(6) : error, "08001"); - } catch (java.net.UnknownHostException e) { - throw new SQLNonTransientConnectionException("Unknown Host (" + hostname + "): " + e.getMessage(), "08006"); } catch (IOException e) { - throw new SQLNonTransientConnectionException("Unable to connect (" + hostname + ":" + port + "): " + e.getMessage(), "08006"); + throw new SQLNonTransientConnectionException("Cannot connect: " + e.getMessage(), "08006"); } catch (MCLParseException e) { throw new SQLNonTransientConnectionException(e.getMessage(), "08001"); } catch (org.monetdb.mcl.MCLException e) { @@ -335,53 +228,17 @@ public class MonetConnection throw sqle; } - // we seem to have managed to log in, let's store the - // language used and language specific query templates - if ("sql".equals(language)) { - lang = LANG_SQL; - - queryTempl[0] = "s"; // pre - queryTempl[1] = "\n;"; // post - queryTempl[2] = "\n;\n"; // separator - - commandTempl[0] = "X"; // pre - commandTempl[1] = ""; // post - //commandTempl[2] = "\nX"; // separator (is not used) - } else if ("mal".equals(language)) { - lang = LANG_MAL; - - queryTempl[0] = ""; // pre - queryTempl[1] = ";\n"; // post - queryTempl[2] = ";\n"; // separator - - commandTempl[0] = ""; // pre - commandTempl[1] = ""; // post - //commandTempl[2] = ""; // separator (is not used) - } else { - lang = LANG_UNKNOWN; - } - // Now take care of any handshake options not handled during the handshake - if (replySizeSetting.isSent()) { - this.curReplySize = replySizeSetting.get(); - } - this.defaultFetchSize = replySizeSetting.get(); + curReplySize = defaultFetchSize; if (lang == LANG_SQL) { - if (autoCommitSetting.mustSend(autoCommit)) { - setAutoCommit(autoCommitSetting.get()); - } else { - // update bookkeeping - autoCommit = autoCommitSetting.get(); + if (autoCommit != target.isAutocommit()) { + setAutoCommit(target.isAutocommit()); } - if (sizeHeaderSetting.mustSend(false)) { + if (!sizeHeaderEnabled) { sendControlCommand("sizeheader 1"); - } else { - // no bookkeeping to update } - if (timeZoneSetting.mustSend(0)) { - setTimezone(timeZoneSetting.get()); - } else { - // no bookkeeping to update + if (!timeZoneSet) { + setTimezone(target.getTimezone()); } } @@ -1780,7 +1637,7 @@ public class MonetConnection * @return whether the JDBC BLOB type should be mapped to VARBINARY type. */ boolean mapBlobAsVarBinary() { - return treatBlobAsVarBinary; + return target.isTreatBlobAsBinary(); } /** @@ -1791,7 +1648,7 @@ public class MonetConnection * @return whether the JDBC CLOB type should be mapped to VARCHAR type. */ boolean mapClobAsVarChar() { - return treatClobAsVarChar; + return target.isTreatClobAsVarchar(); } /** @@ -1800,13 +1657,7 @@ public class MonetConnection * @return the MonetDB JDBC Connection URL (without user name and password). */ String getJDBCURL() { - final StringBuilder sb = new StringBuilder(128); - sb.append("jdbc:monetdb://").append(hostname) - .append(':').append(port) - .append('/').append(database); - if (lang == LANG_MAL) - sb.append("?language=mal"); - return sb.toString(); + return target.buildUrl(); } /** @@ -3887,4 +3738,48 @@ public class MonetConnection super.close(); } } + + public static enum SqlOption { + Autocommit(1, "auto_commit"), + ReplySize(2, "reply_size"), + SizeHeader(3, "size_header"), + // NOTE: 4 has been omitted on purpose + TimeZone(5, "time_zone"), + ; + final int level; + final String field; + + SqlOption(int level, String field) { + this.level = level; + this.field = field; + } + } + + + private class SqlOptionsCallback extends MapiSocket.OptionsCallback { + private int level; + @Override + public void addOptions(String lang, int level) { + if (!lang.equals("sql")) + return; + this.level = level; + + // Try to add options and record that this happened if it succeeds. + if (contribute(SqlOption.Autocommit, target.isAutocommit() ? 1 : 0)) + autoCommit = target.isAutocommit(); + if (contribute(SqlOption.ReplySize, target.getReplysize())) + defaultFetchSize = target.getReplysize(); + if (contribute(SqlOption.SizeHeader, 1)) + sizeHeaderEnabled = true; + if (contribute(SqlOption.TimeZone, target.getTimezone())) + timeZoneSet = true; + } + + private boolean contribute(SqlOption opt, int value) { + if (this.level <= opt.level) + return false; + contribute(opt.field, value); + return true; + } + } }