Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/jdbc/MonetConnection.java @ 391:f523727db392
Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.*
This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12) |
parents | src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java@11c30e3b7966 |
children | bf9f6b6ecf40 |
comparison
equal
deleted
inserted
replaced
390:6199e0be3c6e | 391:f523727db392 |
---|---|
1 /* | |
2 * This Source Code Form is subject to the terms of the Mozilla Public | |
3 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 * | |
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V. | |
7 */ | |
8 | |
9 package org.monetdb.jdbc; | |
10 | |
11 import java.io.File; | |
12 import java.io.IOException; | |
13 import java.net.SocketException; | |
14 import java.net.SocketTimeoutException; | |
15 import java.sql.CallableStatement; | |
16 import java.sql.Connection; | |
17 import java.sql.DatabaseMetaData; | |
18 import java.sql.PreparedStatement; | |
19 import java.sql.ResultSet; | |
20 import java.sql.SQLClientInfoException; | |
21 import java.sql.SQLException; | |
22 import java.sql.SQLFeatureNotSupportedException; | |
23 import java.sql.SQLNonTransientConnectionException; | |
24 import java.sql.SQLWarning; | |
25 import java.sql.Savepoint; | |
26 import java.sql.Statement; | |
27 import java.util.ArrayList; | |
28 import java.util.Calendar; | |
29 import java.util.HashMap; | |
30 import java.util.Map; | |
31 import java.util.Map.Entry; | |
32 import java.util.Properties; | |
33 import java.util.WeakHashMap; | |
34 import java.util.concurrent.Executor; | |
35 | |
36 import org.monetdb.mcl.io.BufferedMCLReader; | |
37 import org.monetdb.mcl.io.BufferedMCLWriter; | |
38 import org.monetdb.mcl.net.MapiSocket; | |
39 import org.monetdb.mcl.parser.HeaderLineParser; | |
40 import org.monetdb.mcl.parser.MCLParseException; | |
41 import org.monetdb.mcl.parser.StartOfHeaderParser; | |
42 | |
43 /** | |
44 * A {@link Connection} suitable for the MonetDB database. | |
45 * | |
46 * This connection represents a connection (session) to a MonetDB | |
47 * database. SQL statements are executed and results are returned within | |
48 * the context of a connection. This Connection object holds a physical | |
49 * connection to the MonetDB database. | |
50 * | |
51 * A Connection object's database should able to provide information | |
52 * describing its tables, its supported SQL grammar, its stored | |
53 * procedures, the capabilities of this connection, and so on. This | |
54 * information is obtained with the getMetaData method. | |
55 * | |
56 * Note: By default a Connection object is in auto-commit mode, which | |
57 * means that it automatically commits changes after executing each | |
58 * statement. If auto-commit mode has been disabled, the method commit | |
59 * must be called explicitly in order to commit changes; otherwise, | |
60 * database changes will not be saved. | |
61 * | |
62 * The current state of this connection is that it nearly implements the | |
63 * whole Connection interface. | |
64 * | |
65 * @author Fabian Groffen | |
66 * @author Martin van Dinther | |
67 * @version 1.5 | |
68 */ | |
69 public class MonetConnection | |
70 extends MonetWrapper | |
71 implements Connection, AutoCloseable | |
72 { | |
73 /** the successful processed input properties */ | |
74 private final Properties conn_props = new Properties(); | |
75 | |
76 /** The hostname to connect to */ | |
77 private final String hostname; | |
78 /** The port to connect on the host to */ | |
79 private int port = 0; | |
80 /** The database to use (currently not used) */ | |
81 private final String database; | |
82 /** The username to use when authenticating */ | |
83 private final String username; | |
84 /** The password to use when authenticating */ | |
85 private final String password; | |
86 | |
87 /** A connection to mserver5 using a TCP socket */ | |
88 private final MapiSocket server; | |
89 /** The Reader from the server */ | |
90 private final BufferedMCLReader in; | |
91 /** The Writer to the server */ | |
92 private final BufferedMCLWriter out; | |
93 | |
94 /** A StartOfHeaderParser declared for reuse. */ | |
95 private StartOfHeaderParser sohp = new StartOfHeaderParser(); | |
96 | |
97 /** Whether this Connection is closed (and cannot be used anymore) */ | |
98 private boolean closed; | |
99 | |
100 /** Whether this Connection is in autocommit mode */ | |
101 private boolean autoCommit = true; | |
102 | |
103 /** The stack of warnings for this Connection object */ | |
104 private SQLWarning warnings = null; | |
105 | |
106 /** The Connection specific mapping of user defined types to Java types */ | |
107 private Map<String,Class<?>> typeMap = new HashMap<String,Class<?>>() { | |
108 private static final long serialVersionUID = 1L; | |
109 { | |
110 put("inet", org.monetdb.jdbc.types.INET.class); | |
111 put("url", org.monetdb.jdbc.types.URL.class); | |
112 } | |
113 }; | |
114 | |
115 // See javadoc for documentation about WeakHashMap if you don't know what | |
116 // it does !!!NOW!!! (only when you deal with it of course) | |
117 /** A Map containing all (active) Statements created from this Connection */ | |
118 private WeakHashMap<Statement,?> statements = new WeakHashMap<Statement, Object>(); | |
119 | |
120 /** The number of results we receive from the server at once */ | |
121 private int curReplySize = -1; // the server by default uses -1 (all) | |
122 | |
123 /** A template to apply to each query (like pre and post fixes), filled in constructor */ | |
124 // note: it is made public to the package as queryTempl[2] is used from MonetStatement | |
125 final String[] queryTempl = new String[3]; // pre, post, sep | |
126 | |
127 /** A template to apply to each command (like pre and post fixes), filled in constructor */ | |
128 private final String[] commandTempl = new String[2]; // pre, post | |
129 | |
130 /** the SQL language */ | |
131 private static final int LANG_SQL = 0; | |
132 /** the MAL language (officially *NOT* supported) */ | |
133 private static final int LANG_MAL = 3; | |
134 /** an unknown language */ | |
135 private static final int LANG_UNKNOWN = -1; | |
136 /** The language which is used */ | |
137 private final int lang; | |
138 | |
139 /** Whether or not BLOB is mapped to Types.VARBINARY instead of Types.BLOB within this connection */ | |
140 private boolean treatBlobAsVarBinary = true; | |
141 /** Whether or not CLOB is mapped to Types.VARCHAR instead of Types.CLOB within this connection */ | |
142 private boolean treatClobAsVarChar = true; | |
143 | |
144 /** The last set query timeout on the server as used by Statement, PreparedStatement and CallableStatement */ | |
145 protected int lastSetQueryTimeout = 0; // 0 means no timeout, which is the default on the server | |
146 | |
147 | |
148 /** | |
149 * Constructor of a Connection for MonetDB. At this moment the | |
150 * current implementation limits itself to storing the given host, | |
151 * database, username and password for later use by the | |
152 * createStatement() call. This constructor is only accessible to | |
153 * classes from the jdbc package. | |
154 * | |
155 * @param props a Property hashtable holding the properties needed for connecting | |
156 * @throws SQLException if a database error occurs | |
157 * @throws IllegalArgumentException is one of the arguments is null or empty | |
158 */ | |
159 MonetConnection(final Properties props) | |
160 throws SQLException, IllegalArgumentException | |
161 { | |
162 // for debug: System.out.println("New connection object. Received properties are: " + props.toString()); | |
163 // get supported property values from the props argument. | |
164 // When a value is found add it to the internal conn_props list for use by getClientInfo(). | |
165 this.hostname = props.getProperty("host"); | |
166 if (this.hostname != null) | |
167 conn_props.setProperty("host", this.hostname); | |
168 | |
169 final String port_prop = props.getProperty("port"); | |
170 if (port_prop != null) { | |
171 try { | |
172 this.port = Integer.parseInt(port_prop); | |
173 } catch (NumberFormatException e) { | |
174 addWarning("Unable to parse port number from: " + port_prop, "M1M05"); | |
175 } | |
176 conn_props.setProperty("port", Integer.toString(this.port)); | |
177 } | |
178 | |
179 this.database = props.getProperty("database"); | |
180 if (this.database != null) | |
181 conn_props.setProperty("database", this.database); | |
182 | |
183 this.username = props.getProperty("user"); | |
184 if (this.username != null) | |
185 conn_props.setProperty("user", this.username); | |
186 | |
187 this.password = props.getProperty("password"); | |
188 if (this.password != null) | |
189 conn_props.setProperty("password", this.password); | |
190 | |
191 String language = props.getProperty("language"); | |
192 if (language != null) | |
193 conn_props.setProperty("language", language); | |
194 | |
195 boolean debug = false; | |
196 String debug_prop = props.getProperty("debug"); | |
197 if (debug_prop != null) { | |
198 debug = Boolean.parseBoolean(debug_prop); | |
199 conn_props.setProperty("debug", Boolean.toString(debug)); | |
200 } | |
201 | |
202 final String hash = props.getProperty("hash"); | |
203 if (hash != null) | |
204 conn_props.setProperty("hash", hash); | |
205 | |
206 final String treatBlobAsVarBinary_prop = props.getProperty("treat_blob_as_binary"); | |
207 if (treatBlobAsVarBinary_prop != null) { | |
208 treatBlobAsVarBinary = Boolean.parseBoolean(treatBlobAsVarBinary_prop); | |
209 conn_props.setProperty("treat_blob_as_binary", Boolean.toString(treatBlobAsVarBinary)); | |
210 if (treatBlobAsVarBinary) | |
211 typeMap.put("blob", Byte[].class); | |
212 } | |
213 | |
214 final String treatClobAsVarChar_prop = props.getProperty("treat_clob_as_varchar"); | |
215 if (treatClobAsVarChar_prop != null) { | |
216 treatClobAsVarChar = Boolean.parseBoolean(treatClobAsVarChar_prop); | |
217 conn_props.setProperty("treat_clob_as_varchar", Boolean.toString(treatClobAsVarChar)); | |
218 if (treatClobAsVarChar) | |
219 typeMap.put("clob", String.class); | |
220 } | |
221 | |
222 int sockTimeout = 0; | |
223 final String so_timeout_prop = props.getProperty("so_timeout"); | |
224 if (so_timeout_prop != null) { | |
225 try { | |
226 sockTimeout = Integer.parseInt(so_timeout_prop); | |
227 if (sockTimeout < 0) { | |
228 addWarning("Negative socket timeout not allowed. Value ignored", "M1M05"); | |
229 sockTimeout = 0; | |
230 } | |
231 } catch (NumberFormatException e) { | |
232 addWarning("Unable to parse socket timeout number from: " + so_timeout_prop, "M1M05"); | |
233 } | |
234 conn_props.setProperty("so_timeout", Integer.toString(sockTimeout)); | |
235 } | |
236 | |
237 // check mandatory input arguments | |
238 if (hostname == null || hostname.isEmpty()) | |
239 throw new IllegalArgumentException("Missing or empty host name"); | |
240 if (port <= 0 || port > 65535) | |
241 throw new IllegalArgumentException("Invalid port number: " + port | |
242 + ". It should not be " + (port < 0 ? "negative" : (port > 65535 ? "larger than 65535" : "0"))); | |
243 if (username == null || username.isEmpty()) | |
244 throw new IllegalArgumentException("Missing or empty user name"); | |
245 if (password == null || password.isEmpty()) | |
246 throw new IllegalArgumentException("Missing or empty password"); | |
247 if (language == null || language.isEmpty()) { | |
248 // fallback to default language: sql | |
249 language = "sql"; | |
250 addWarning("No language specified, defaulting to 'sql'", "M1M05"); | |
251 } | |
252 | |
253 server = new MapiSocket(); | |
254 if (hash != null) | |
255 server.setHash(hash); | |
256 if (database != null) | |
257 server.setDatabase(database); | |
258 server.setLanguage(language); | |
259 | |
260 // we're debugging here... uhm, should be off in real life | |
261 if (debug) { | |
262 try { | |
263 final String fname = props.getProperty("logfile", "monet_" + System.currentTimeMillis() + ".log"); | |
264 File f = new File(fname); | |
265 | |
266 int ext = fname.lastIndexOf('.'); | |
267 if (ext < 0) | |
268 ext = fname.length(); | |
269 final String pre = fname.substring(0, ext); | |
270 final String suf = fname.substring(ext); | |
271 | |
272 for (int i = 1; f.exists(); i++) { | |
273 f = new File(pre + "-" + i + suf); | |
274 } | |
275 | |
276 server.debug(f.getAbsolutePath()); // enable logging on the MapiSocket level | |
277 } catch (IOException ex) { | |
278 throw new SQLNonTransientConnectionException("Opening logfile failed: " + ex.getMessage(), "08M01"); | |
279 } | |
280 } | |
281 | |
282 try { | |
283 final java.util.List<String> warnings = server.connect(hostname, port, username, password); | |
284 for (String warning : warnings) { | |
285 addWarning(warning, "01M02"); | |
286 } | |
287 | |
288 // apply NetworkTimeout value from legacy (pre 4.1) driver | |
289 // so_timeout calls | |
290 server.setSoTimeout(sockTimeout); | |
291 | |
292 in = server.getReader(); | |
293 out = server.getWriter(); | |
294 | |
295 final String error = in.waitForPrompt(); | |
296 if (error != null) | |
297 throw new SQLNonTransientConnectionException((error.length() > 6) ? error.substring(6) : error, "08001"); | |
298 } catch (java.net.UnknownHostException e) { | |
299 throw new SQLNonTransientConnectionException("Unknown Host (" + hostname + "): " + e.getMessage(), "08006"); | |
300 } catch (IOException e) { | |
301 throw new SQLNonTransientConnectionException("Unable to connect (" + hostname + ":" + port + "): " + e.getMessage(), "08006"); | |
302 } catch (MCLParseException e) { | |
303 throw new SQLNonTransientConnectionException(e.getMessage(), "08001"); | |
304 } catch (org.monetdb.mcl.MCLException e) { | |
305 final String[] connex = e.getMessage().split("\n"); | |
306 final SQLException sqle = new SQLNonTransientConnectionException(connex[0], "08001", e); | |
307 for (int i = 1; i < connex.length; i++) { | |
308 sqle.setNextException(new SQLNonTransientConnectionException(connex[1], "08001")); | |
309 } | |
310 throw sqle; | |
311 } | |
312 | |
313 // we seem to have managed to log in, let's store the | |
314 // language used and language specific query templates | |
315 if ("sql".equals(language)) { | |
316 lang = LANG_SQL; | |
317 | |
318 queryTempl[0] = "s"; // pre | |
319 queryTempl[1] = "\n;"; // post | |
320 queryTempl[2] = "\n;\n"; // separator | |
321 | |
322 commandTempl[0] = "X"; // pre | |
323 commandTempl[1] = ""; // post | |
324 //commandTempl[2] = "\nX"; // separator (is not used) | |
325 } else if ("mal".equals(language)) { | |
326 lang = LANG_MAL; | |
327 | |
328 queryTempl[0] = ""; // pre | |
329 queryTempl[1] = ";\n"; // post | |
330 queryTempl[2] = ";\n"; // separator | |
331 | |
332 commandTempl[0] = ""; // pre | |
333 commandTempl[1] = ""; // post | |
334 //commandTempl[2] = ""; // separator (is not used) | |
335 } else { | |
336 lang = LANG_UNKNOWN; | |
337 } | |
338 | |
339 // the following initialisers are only valid when the language is SQL... | |
340 if (lang == LANG_SQL) { | |
341 // enable auto commit | |
342 setAutoCommit(true); | |
343 | |
344 // set our time zone on the server | |
345 final Calendar cal = Calendar.getInstance(); | |
346 int offset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET); | |
347 offset /= (60 * 1000); // milliseconds to minutes | |
348 String tz = offset < 0 ? "-" : "+"; | |
349 tz += (Math.abs(offset) / 60 < 10 ? "0" : "") + (Math.abs(offset) / 60) + ":"; | |
350 offset -= (offset / 60) * 60; | |
351 tz += (offset < 10 ? "0" : "") + offset; | |
352 sendIndependentCommand("SET TIME ZONE INTERVAL '" + tz + "' HOUR TO MINUTE"); | |
353 } | |
354 | |
355 // we're absolutely not closed, since we're brand new | |
356 closed = false; | |
357 } | |
358 | |
359 //== methods of interface Connection | |
360 | |
361 /** | |
362 * Clears all warnings reported for this Connection object. After a | |
363 * call to this method, the method getWarnings returns null until a | |
364 * new warning is reported for this Connection object. | |
365 */ | |
366 @Override | |
367 public void clearWarnings() { | |
368 warnings = null; | |
369 } | |
370 | |
371 /** | |
372 * Releases this Connection object's database and JDBC resources | |
373 * immediately instead of waiting for them to be automatically | |
374 * released. All Statements created from this Connection will be | |
375 * closed when this method is called. | |
376 * | |
377 * Calling the method close on a Connection object that is already | |
378 * closed is a no-op. | |
379 */ | |
380 @Override | |
381 public void close() { | |
382 synchronized (server) { | |
383 for (Statement st : statements.keySet()) { | |
384 try { | |
385 st.close(); | |
386 } catch (SQLException e) { | |
387 // better luck next time! | |
388 } | |
389 } | |
390 // close the socket | |
391 server.close(); | |
392 // report ourselves as closed | |
393 closed = true; | |
394 } | |
395 } | |
396 | |
397 /** | |
398 * Makes all changes made since the previous commit/rollback | |
399 * permanent and releases any database locks currently held by this | |
400 * Connection object. This method should be used only when | |
401 * auto-commit mode has been disabled. | |
402 * | |
403 * @throws SQLException if a database access error occurs or this | |
404 * Connection object is in auto-commit mode | |
405 * @see #setAutoCommit(boolean) | |
406 */ | |
407 @Override | |
408 public void commit() throws SQLException { | |
409 // note: can't use sendIndependentCommand here because we need | |
410 // to process the auto_commit state the server gives | |
411 sendTransactionCommand("COMMIT"); | |
412 } | |
413 | |
414 /** | |
415 * Creates a Statement object for sending SQL statements to the | |
416 * database. SQL statements without parameters are normally | |
417 * executed using Statement objects. If the same SQL statement is | |
418 * executed many times, it may be more efficient to use a | |
419 * PreparedStatement object. | |
420 * | |
421 * Result sets created using the returned Statement object will by | |
422 * default be type TYPE_FORWARD_ONLY and have a concurrency level of | |
423 * CONCUR_READ_ONLY and a holdability of HOLD_CURSORS_OVER_COMMIT. | |
424 * | |
425 * @return a new default Statement object | |
426 * @throws SQLException if a database access error occurs | |
427 */ | |
428 @Override | |
429 public Statement createStatement() throws SQLException { | |
430 return createStatement(MonetResultSet.DEF_RESULTSETTYPE, MonetResultSet.DEF_CONCURRENCY, MonetResultSet.DEF_HOLDABILITY); | |
431 } | |
432 | |
433 /** | |
434 * Creates a Statement object that will generate ResultSet objects | |
435 * with the given type and concurrency. This method is the same as | |
436 * the createStatement method above, but it allows the default | |
437 * result set type and concurrency to be overridden. | |
438 * | |
439 * @param resultSetType a result set type; one of | |
440 * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, | |
441 * or ResultSet.TYPE_SCROLL_SENSITIVE | |
442 * @param resultSetConcurrency a concurrency type; one of | |
443 * ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE | |
444 * @return a new Statement object that will generate ResultSet objects with | |
445 * the given type and concurrency | |
446 * @throws SQLException if a database access error occurs | |
447 */ | |
448 @Override | |
449 public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException { | |
450 return createStatement(resultSetType, resultSetConcurrency, MonetResultSet.DEF_HOLDABILITY); | |
451 } | |
452 | |
453 /** | |
454 * Creates a Statement object that will generate ResultSet objects | |
455 * with the given type, concurrency, and holdability. This method | |
456 * is the same as the createStatement method above, but it allows | |
457 * the default result set type, concurrency, and holdability to be | |
458 * overridden. | |
459 * | |
460 * @param resultSetType one of the following ResultSet constants: | |
461 * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, | |
462 * or ResultSet.TYPE_SCROLL_SENSITIVE | |
463 * @param resultSetConcurrency one of the following ResultSet | |
464 * constants: ResultSet.CONCUR_READ_ONLY or | |
465 * ResultSet.CONCUR_UPDATABLE | |
466 * @param resultSetHoldability one of the following ResultSet | |
467 * constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or | |
468 * ResultSet.CLOSE_CURSORS_AT_COMMIT | |
469 * | |
470 * @return a new Statement object that will generate ResultSet | |
471 * objects with the given type, concurrency, and holdability | |
472 * @throws SQLException if a database access error occurs or the | |
473 * given parameters are not ResultSet constants indicating type, | |
474 * concurrency, and holdability | |
475 */ | |
476 @Override | |
477 public Statement createStatement(final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException { | |
478 try { | |
479 final Statement ret = new MonetStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability); | |
480 // store it in the map for when we close... | |
481 statements.put(ret, null); | |
482 return ret; | |
483 } catch (IllegalArgumentException e) { | |
484 throw new SQLException(e.toString(), "M0M03"); | |
485 } | |
486 // we don't have to catch SQLException because that is declared to be thrown | |
487 } | |
488 | |
489 /** | |
490 * Retrieves the current auto-commit mode for this Connection object. | |
491 * | |
492 * @return the current state of this Connection object's auto-commit mode | |
493 * @see #setAutoCommit(boolean) | |
494 */ | |
495 @Override | |
496 public boolean getAutoCommit() { | |
497 return autoCommit; | |
498 } | |
499 | |
500 /** | |
501 * Retrieves this Connection object's current catalog name. | |
502 * | |
503 * @return the current catalog name or null if there is none | |
504 */ | |
505 @Override | |
506 public String getCatalog() { | |
507 // MonetDB does NOT support catalogs | |
508 return null; | |
509 } | |
510 | |
511 /** | |
512 * Retrieves the current holdability of ResultSet objects created | |
513 * using this Connection object. | |
514 * | |
515 * @return the holdability, one of | |
516 * ResultSet.HOLD_CURSORS_OVER_COMMIT or | |
517 * ResultSet.CLOSE_CURSORS_AT_COMMIT | |
518 * @see #setHoldability(int) | |
519 */ | |
520 @Override | |
521 public int getHoldability() { | |
522 // TODO: perhaps it is better to have the server implement | |
523 // CLOSE_CURSORS_AT_COMMIT | |
524 return ResultSet.HOLD_CURSORS_OVER_COMMIT; | |
525 } | |
526 | |
527 /** | |
528 * Retrieves a DatabaseMetaData object that contains metadata about | |
529 * the database to which this Connection object represents a | |
530 * connection. The metadata includes information about the | |
531 * database's tables, its supported SQL grammar, its stored | |
532 * procedures, the capabilities of this connection, and so on. | |
533 * | |
534 * @throws SQLException if the current language is not SQL | |
535 * @return a DatabaseMetaData object for this Connection object | |
536 */ | |
537 @Override | |
538 public DatabaseMetaData getMetaData() throws SQLException { | |
539 if (lang != LANG_SQL) | |
540 throw new SQLException("This method is only supported in SQL mode", "M0M04"); | |
541 | |
542 return new MonetDatabaseMetaData(this); | |
543 } | |
544 | |
545 /** | |
546 * Retrieves this Connection object's current transaction isolation | |
547 * level. | |
548 * | |
549 * @return the current transaction isolation level, which will be | |
550 * Connection.TRANSACTION_SERIALIZABLE | |
551 */ | |
552 @Override | |
553 public int getTransactionIsolation() { | |
554 return TRANSACTION_SERIALIZABLE; | |
555 } | |
556 | |
557 /** | |
558 * Retrieves the Map object associated with this Connection object. | |
559 * Unless the application has added an entry, the type map returned | |
560 * will be empty. | |
561 * | |
562 * @return the java.util.Map object associated with this Connection | |
563 * object | |
564 */ | |
565 @Override | |
566 public Map<String,Class<?>> getTypeMap() { | |
567 return typeMap; | |
568 } | |
569 | |
570 /** | |
571 * Retrieves the first warning reported by calls on this Connection | |
572 * object. If there is more than one warning, subsequent warnings | |
573 * will be chained to the first one and can be retrieved by calling | |
574 * the method SQLWarning.getNextWarning on the warning that was | |
575 * retrieved previously. | |
576 * | |
577 * This method may not be called on a closed connection; doing so | |
578 * will cause an SQLException to be thrown. | |
579 * | |
580 * Note: Subsequent warnings will be chained to this SQLWarning. | |
581 * | |
582 * @return the first SQLWarning object or null if there are none | |
583 * @throws SQLException if a database access error occurs or this method is | |
584 * called on a closed connection | |
585 */ | |
586 @Override | |
587 public SQLWarning getWarnings() throws SQLException { | |
588 checkNotClosed(); | |
589 // if there are no warnings, this will be null, which fits with the | |
590 // specification. | |
591 return warnings; | |
592 } | |
593 | |
594 /** | |
595 * Retrieves whether this Connection object has been closed. A | |
596 * connection is closed if the method close has been called on it or | |
597 * if certain fatal errors have occurred. This method is guaranteed | |
598 * to return true only when it is called after the method | |
599 * Connection.close has been called. | |
600 * | |
601 * This method generally cannot be called to determine whether a | |
602 * connection to a database is valid or invalid. A typical client | |
603 * can determine that a connection is invalid by catching any | |
604 * exceptions that might be thrown when an operation is attempted. | |
605 * | |
606 * @return true if this Connection object is closed; false if it is | |
607 * still open | |
608 */ | |
609 @Override | |
610 public boolean isClosed() { | |
611 return closed; | |
612 } | |
613 | |
614 /** | |
615 * Retrieves whether this Connection object is in read-only mode. | |
616 * MonetDB currently doesn't support updateable result sets, but | |
617 * updates are possible. Hence the Connection object is never in | |
618 * read-only mode. | |
619 * | |
620 * @return true if this Connection object is read-only; false otherwise | |
621 */ | |
622 @Override | |
623 public boolean isReadOnly() { | |
624 return false; | |
625 } | |
626 | |
627 /** | |
628 * Converts the given SQL statement into the system's native SQL grammar. | |
629 * A driver may convert the JDBC SQL grammar into its system's native SQL grammar prior to sending it. | |
630 * This method returns the native form of the statement that the driver would have sent. | |
631 * | |
632 * @param sql - an SQL statement that may contain one or more '?' parameter placeholders. | |
633 * @return the native form of this statement | |
634 */ | |
635 @Override | |
636 public String nativeSQL(final String sql) { | |
637 /* there is currently no way to get the native MonetDB rewritten SQL string back, so just return the original string */ | |
638 /* in future we may replace/remove the escape sequences { <escape-type> ...} before sending it to the server */ | |
639 return sql; | |
640 } | |
641 | |
642 /** | |
643 * Creates a CallableStatement object for calling database stored procedures. | |
644 * The CallableStatement object provides methods for setting up its IN and OUT parameters, | |
645 * and methods for executing the call to a stored procedure. | |
646 * | |
647 * Note: This method is optimized for handling stored procedure call statements. | |
648 * Some drivers may send the call statement to the database when the method prepareCall is done; | |
649 * others may wait until the CallableStatement object is executed. This has no direct effect | |
650 * on users; however, it does affect which method throws certain SQLExceptions. | |
651 * | |
652 * Result sets created using the returned CallableStatement object will by default be type TYPE_FORWARD_ONLY | |
653 * and have a concurrency level of CONCUR_READ_ONLY. | |
654 * The holdability of the created result sets can be determined by calling getHoldability(). | |
655 * | |
656 * @param sql - an SQL statement that may contain one or more '?' parameter placeholders. | |
657 * Typically this statement is specified using JDBC call escape syntax. | |
658 * @return a new default CallableStatement object containing the pre-compiled SQL statement | |
659 * @throws SQLException - if a database access error occurs or this method is called on a closed connection | |
660 */ | |
661 @Override | |
662 public CallableStatement prepareCall(final String sql) throws SQLException { | |
663 return prepareCall(sql, MonetResultSet.DEF_RESULTSETTYPE, MonetResultSet.DEF_CONCURRENCY, MonetResultSet.DEF_HOLDABILITY); | |
664 } | |
665 | |
666 /** | |
667 * Creates a CallableStatement object that will generate ResultSet objects with the given type and concurrency. | |
668 * This method is the same as the prepareCall method above, but it allows the default result set type and concurrency to be overridden. | |
669 * The holdability of the created result sets can be determined by calling getHoldability(). | |
670 * | |
671 * @param sql - a String object that is the SQL statement to be sent to the database; may contain on or more '?' parameters | |
672 * Typically this statement is specified using JDBC call escape syntax. | |
673 * @param resultSetType - a result set type; one of ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE | |
674 * @param resultSetConcurrency - a concurrency type; one of ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE | |
675 * @return a new CallableStatement object containing the pre-compiled SQL statement that | |
676 * will produce ResultSet objects with the given type and concurrency | |
677 * @throws SQLException - if a database access error occurs, this method is called on a closed connection or | |
678 * the given parameters are not ResultSet constants indicating type and concurrency | |
679 */ | |
680 @Override | |
681 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException { | |
682 return prepareCall(sql, resultSetType, resultSetConcurrency, MonetResultSet.DEF_HOLDABILITY); | |
683 } | |
684 | |
685 /** | |
686 * Creates a CallableStatement object that will generate ResultSet objects with the given type and concurrency. | |
687 * This method is the same as the prepareCall method above, but it allows the default result set type, result set concurrency type and holdability to be overridden. | |
688 * | |
689 * @param sql - a String object that is the SQL statement to be sent to the database; may contain on or more '?' parameters | |
690 * Typically this statement is specified using JDBC call escape syntax. | |
691 * @param resultSetType - a result set type; one of ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE | |
692 * @param resultSetConcurrency - a concurrency type; one of ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE | |
693 * @param resultSetHoldability - one of the following ResultSet constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT | |
694 * @return a new CallableStatement object, containing the pre-compiled SQL statement, that will generate ResultSet objects with the given type, concurrency, and holdability | |
695 * @throws SQLException - if a database access error occurs, this method is called on a closed connection or | |
696 * the given parameters are not ResultSet constants indicating type, concurrency, and holdability | |
697 */ | |
698 @Override | |
699 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) | |
700 throws SQLException | |
701 { | |
702 checkNotClosed(); | |
703 try { | |
704 final CallableStatement ret = new MonetCallableStatement( | |
705 this, | |
706 resultSetType, | |
707 resultSetConcurrency, | |
708 resultSetHoldability, | |
709 sql | |
710 ); | |
711 // store it in the map for when we close... | |
712 statements.put(ret, null); | |
713 return ret; | |
714 } catch (IllegalArgumentException e) { | |
715 throw new SQLException(e.toString(), "M0M03"); | |
716 } | |
717 // we don't have to catch SQLException because that is declared to be thrown | |
718 } | |
719 | |
720 /** | |
721 * Creates a PreparedStatement object for sending parameterized SQL | |
722 * statements to the database. | |
723 * | |
724 * A SQL statement with or without IN parameters can be pre-compiled | |
725 * and stored in a PreparedStatement object. This object can then be | |
726 * used to efficiently execute this statement multiple times. | |
727 * | |
728 * Note: This method is optimized for handling parametric SQL | |
729 * statements that benefit from precompilation. If the driver | |
730 * supports precompilation, the method prepareStatement will send | |
731 * the statement to the database for precompilation. Some drivers | |
732 * may not support precompilation. In this case, the statement may | |
733 * not be sent to the database until the PreparedStatement object is | |
734 * executed. This has no direct effect on users; however, it does | |
735 * affect which methods throw certain SQLException objects. | |
736 * | |
737 * Result sets created using the returned PreparedStatement object | |
738 * will by default be type TYPE_FORWARD_ONLY and have a concurrency | |
739 * level of CONCUR_READ_ONLY. | |
740 * | |
741 * @param sql an SQL statement that may contain one or more '?' IN | |
742 * parameter placeholders | |
743 * @return a new default PreparedStatement object containing the | |
744 * pre-compiled SQL statement | |
745 * @throws SQLException if a database access error occurs | |
746 */ | |
747 @Override | |
748 public PreparedStatement prepareStatement(final String sql) throws SQLException { | |
749 return prepareStatement(sql, MonetResultSet.DEF_RESULTSETTYPE, MonetResultSet.DEF_CONCURRENCY, MonetResultSet.DEF_HOLDABILITY); | |
750 } | |
751 | |
752 /** | |
753 * Creates a PreparedStatement object that will generate ResultSet | |
754 * objects with the given type and concurrency. This method is the | |
755 * same as the prepareStatement method above, but it allows the | |
756 * default result set type and concurrency to be overridden. | |
757 * | |
758 * @param sql a String object that is the SQL statement to be sent to the | |
759 * database; may contain one or more ? IN parameters | |
760 * @param resultSetType a result set type; one of | |
761 * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, | |
762 * or ResultSet.TYPE_SCROLL_SENSITIVE | |
763 * @param resultSetConcurrency a concurrency type; one of | |
764 * ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE | |
765 * @return a new PreparedStatement object containing the pre-compiled SQL | |
766 * statement that will produce ResultSet objects with the given | |
767 * type and concurrency | |
768 * @throws SQLException if a database access error occurs or the given | |
769 * parameters are not ResultSet constants indicating | |
770 * type and concurrency | |
771 */ | |
772 @Override | |
773 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException { | |
774 return prepareStatement(sql, resultSetType, resultSetConcurrency, MonetResultSet.DEF_HOLDABILITY); | |
775 } | |
776 | |
777 /** | |
778 * Creates a PreparedStatement object that will generate ResultSet | |
779 * objects with the given type, concurrency, and holdability. | |
780 * | |
781 * This method is the same as the prepareStatement method above, but | |
782 * it allows the default result set type, concurrency, and | |
783 * holdability to be overridden. | |
784 * | |
785 * @param sql a String object that is the SQL statement to be sent | |
786 * to the database; may contain one or more ? IN parameters | |
787 * @param resultSetType one of the following ResultSet constants: | |
788 * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, | |
789 * or ResultSet.TYPE_SCROLL_SENSITIVE | |
790 * @param resultSetConcurrency one of the following ResultSet | |
791 * constants: ResultSet.CONCUR_READ_ONLY or | |
792 * ResultSet.CONCUR_UPDATABLE | |
793 * @param resultSetHoldability one of the following ResultSet | |
794 * constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or | |
795 * ResultSet.CLOSE_CURSORS_AT_COMMIT | |
796 * @return a new PreparedStatement object, containing the | |
797 * pre-compiled SQL statement, that will generate ResultSet objects | |
798 * with the given type, concurrency, and holdability | |
799 * @throws SQLException if a database access error occurs or the | |
800 * given parameters are not ResultSet constants indicating type, | |
801 * concurrency, and holdability | |
802 */ | |
803 @Override | |
804 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) | |
805 throws SQLException | |
806 { | |
807 checkNotClosed(); | |
808 try { | |
809 final PreparedStatement ret = new MonetPreparedStatement( | |
810 this, | |
811 resultSetType, | |
812 resultSetConcurrency, | |
813 resultSetHoldability, | |
814 sql | |
815 ); | |
816 // store it in the map for when we close... | |
817 statements.put(ret, null); | |
818 return ret; | |
819 } catch (IllegalArgumentException e) { | |
820 throw new SQLException(e.toString(), "M0M03"); | |
821 } | |
822 // we don't have to catch SQLException because that is declared to be thrown | |
823 } | |
824 | |
825 /** | |
826 * Creates a default PreparedStatement object that has the | |
827 * capability to retrieve auto-generated keys. The given constant | |
828 * tells the driver whether it should make auto-generated keys | |
829 * available for retrieval. This parameter is ignored if the SQL | |
830 * statement is not an INSERT statement. | |
831 * | |
832 * Note: This method is optimized for handling parametric SQL | |
833 * statements that benefit from precompilation. If the driver | |
834 * supports precompilation, the method prepareStatement will send | |
835 * the statement to the database for precompilation. Some drivers | |
836 * may not support precompilation. In this case, the statement may | |
837 * not be sent to the database until the PreparedStatement object is | |
838 * executed. This has no direct effect on users; however, it does | |
839 * affect which methods throw certain SQLExceptions. | |
840 * | |
841 * Result sets created using the returned PreparedStatement object | |
842 * will by default be type TYPE_FORWARD_ONLY and have a concurrency | |
843 * level of CONCUR_READ_ONLY. | |
844 * | |
845 * @param sql an SQL statement that may contain one or more '?' IN | |
846 * parameter placeholders | |
847 * @param autoGeneratedKeys a flag indicating whether auto-generated | |
848 * keys should be returned; one of | |
849 * Statement.RETURN_GENERATED_KEYS or | |
850 * Statement.NO_GENERATED_KEYS | |
851 * @return a new PreparedStatement object, containing the | |
852 * pre-compiled SQL statement, that will have the capability | |
853 * of returning auto-generated keys | |
854 * @throws SQLException - if a database access error occurs or the | |
855 * given parameter is not a Statement constant indicating | |
856 * whether auto-generated keys should be returned | |
857 */ | |
858 @Override | |
859 public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { | |
860 if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && | |
861 autoGeneratedKeys != Statement.NO_GENERATED_KEYS) | |
862 throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); | |
863 | |
864 /* MonetDB has no way to disable this, so just do the normal thing ;) */ | |
865 return prepareStatement(sql, MonetResultSet.DEF_RESULTSETTYPE, MonetResultSet.DEF_CONCURRENCY, MonetResultSet.DEF_HOLDABILITY); | |
866 } | |
867 | |
868 /** | |
869 * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. | |
870 * This array contains the indexes of the columns in the target table that contain the auto-generated keys that should be made available. | |
871 * The driver will ignore the array if the SQL statement is not an INSERT statement, or an SQL statement able to | |
872 * return auto-generated keys (the list of such statements is vendor-specific). | |
873 * | |
874 * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. | |
875 * This object can then be used to efficiently execute this statement multiple times. | |
876 * | |
877 * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. | |
878 * If the driver supports precompilation, the method prepareStatement will send the statement to the database for precompilation. | |
879 * Some drivers may not support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement | |
880 * object is executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. | |
881 * | |
882 * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have | |
883 * a concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling getHoldability(). | |
884 * | |
885 * @param sql - an SQL statement that may contain one or more '?' IN parameter placeholders | |
886 * @param columnIndexes - an array of column indexes indicating the columns that should be returned from the inserted row or rows | |
887 * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of | |
888 * returning the auto-generated keys designated by the given array of column indexes | |
889 * @throws SQLException - if a database access error occurs or this method is called on a closed connection | |
890 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this method | |
891 */ | |
892 @Override | |
893 public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException { | |
894 throw newSQLFeatureNotSupportedException("prepareStatement(String sql, int[] columnIndexes)"); | |
895 } | |
896 | |
897 /** | |
898 * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. | |
899 * This array contains the names of the columns in the target table that contain the auto-generated keys that should be returned. | |
900 * The driver will ignore the array if the SQL statement is not an INSERT statement, or an SQL statement able to | |
901 * return auto-generated keys (the list of such statements is vendor-specific). | |
902 * | |
903 * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. | |
904 * This object can then be used to efficiently execute this statement multiple times. | |
905 * | |
906 * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. | |
907 * If the driver supports precompilation, the method prepareStatement will send the statement to the database for precompilation. | |
908 * Some drivers may not support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement | |
909 * object is executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. | |
910 * | |
911 * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have | |
912 * a concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling getHoldability(). | |
913 * | |
914 * @param sql - an SQL statement that may contain one or more '?' IN parameter placeholders | |
915 * @param columnNames - an array of column names indicating the columns that should be returned from the inserted row or rows | |
916 * @return a new PreparedStatement object, containing the pre-compiled statement, that is capable of | |
917 * returning the auto-generated keys designated by the given array of column names | |
918 * @throws SQLException - if a database access error occurs or this method is called on a closed connection | |
919 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this method | |
920 */ | |
921 @Override | |
922 public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException { | |
923 throw newSQLFeatureNotSupportedException("prepareStatement(String sql, String[] columnNames)"); | |
924 } | |
925 | |
926 /** | |
927 * Removes the given Savepoint object from the current transaction. | |
928 * Any reference to the savepoint after it have been removed will | |
929 * cause an SQLException to be thrown. | |
930 * | |
931 * @param savepoint the Savepoint object to be removed | |
932 * @throws SQLException if a database access error occurs or the given | |
933 * Savepoint object is not a valid savepoint in the current | |
934 * transaction | |
935 */ | |
936 @Override | |
937 public void releaseSavepoint(final Savepoint savepoint) throws SQLException { | |
938 checkNotClosed(); | |
939 if (!(savepoint instanceof MonetSavepoint)) | |
940 throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); | |
941 | |
942 final MonetSavepoint sp = (MonetSavepoint)savepoint; | |
943 | |
944 // note: can't use sendIndependentCommand here because we need | |
945 // to process the auto_commit state the server gives | |
946 sendTransactionCommand("RELEASE SAVEPOINT " + sp.getName()); | |
947 } | |
948 | |
949 /** | |
950 * Undoes all changes made in the current transaction and releases | |
951 * any database locks currently held by this Connection object. | |
952 * This method should be used only when auto-commit mode has been disabled. | |
953 * | |
954 * @throws SQLException if a database access error occurs or this | |
955 * Connection object is in auto-commit mode | |
956 * @see #setAutoCommit(boolean) | |
957 */ | |
958 @Override | |
959 public void rollback() throws SQLException { | |
960 checkNotClosed(); | |
961 // note: can't use sendIndependentCommand here because we need | |
962 // to process the auto_commit state the server gives | |
963 sendTransactionCommand("ROLLBACK"); | |
964 } | |
965 | |
966 /** | |
967 * Undoes all changes made after the given Savepoint object was set. | |
968 * | |
969 * This method should be used only when auto-commit has been disabled. | |
970 * | |
971 * @param savepoint the Savepoint object to roll back to | |
972 * @throws SQLException if a database access error occurs, the | |
973 * Savepoint object is no longer valid, or this Connection | |
974 * object is currently in auto-commit mode | |
975 */ | |
976 @Override | |
977 public void rollback(final Savepoint savepoint) throws SQLException { | |
978 checkNotClosed(); | |
979 if (!(savepoint instanceof MonetSavepoint)) | |
980 throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); | |
981 | |
982 final MonetSavepoint sp = (MonetSavepoint)savepoint; | |
983 | |
984 // note: can't use sendIndependentCommand here because we need | |
985 // to process the auto_commit state the server gives | |
986 sendTransactionCommand("ROLLBACK TO SAVEPOINT " + sp.getName()); | |
987 } | |
988 | |
989 /** | |
990 * Sets this connection's auto-commit mode to the given state. If a | |
991 * connection is in auto-commit mode, then all its SQL statements | |
992 * will be executed and committed as individual transactions. | |
993 * Otherwise, its SQL statements are grouped into transactions that | |
994 * are terminated by a call to either the method commit or the | |
995 * method rollback. By default, new connections are in auto-commit mode. | |
996 * | |
997 * The commit occurs when the statement completes or the next | |
998 * execute occurs, whichever comes first. In the case of statements | |
999 * returning a ResultSet object, the statement completes when the | |
1000 * last row of the ResultSet object has been retrieved or the | |
1001 * ResultSet object has been closed. In advanced cases, a single | |
1002 * statement may return multiple results as well as output parameter | |
1003 * values. In these cases, the commit occurs when all results and | |
1004 * output parameter values have been retrieved. | |
1005 * | |
1006 * NOTE: If this method is called during a transaction, the | |
1007 * transaction is committed. | |
1008 * | |
1009 * @param autoCommit true to enable auto-commit mode; false to disable it | |
1010 * @throws SQLException if a database access error occurs | |
1011 * @see #getAutoCommit() | |
1012 */ | |
1013 @Override | |
1014 public void setAutoCommit(final boolean autoCommit) throws SQLException { | |
1015 checkNotClosed(); | |
1016 if (this.autoCommit != autoCommit) { | |
1017 sendControlCommand("auto_commit " + (autoCommit ? "1" : "0")); | |
1018 this.autoCommit = autoCommit; | |
1019 } | |
1020 } | |
1021 | |
1022 /** | |
1023 * Sets the given catalog name in order to select a subspace of this | |
1024 * Connection object's database in which to work. If the driver | |
1025 * does not support catalogs, it will silently ignore this request. | |
1026 */ | |
1027 @Override | |
1028 public void setCatalog(final String catalog) { | |
1029 // silently ignore this request as MonetDB does not support catalogs | |
1030 } | |
1031 | |
1032 /** | |
1033 * Changes the default holdability of ResultSet objects created using this | |
1034 * Connection object to the given holdability. The default holdability of | |
1035 * ResultSet objects can be be determined by invoking DatabaseMetaData.getResultSetHoldability(). | |
1036 * | |
1037 * @param holdability - a ResultSet holdability constant; one of | |
1038 * ResultSet.HOLD_CURSORS_OVER_COMMIT or | |
1039 * ResultSet.CLOSE_CURSORS_AT_COMMIT | |
1040 * @throws SQLException - if a database access error occurs or this method is called on a closed connection | |
1041 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this method or argument | |
1042 * @see #getHoldability() | |
1043 */ | |
1044 @Override | |
1045 public void setHoldability(final int holdability) throws SQLException { | |
1046 // we only support ResultSet.HOLD_CURSORS_OVER_COMMIT | |
1047 if (holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT) | |
1048 throw newSQLFeatureNotSupportedException("setHoldability(CLOSE_CURSORS_AT_COMMIT)"); | |
1049 } | |
1050 | |
1051 /** | |
1052 * Puts this connection in read-only mode as a hint to the driver to | |
1053 * enable database optimizations. MonetDB doesn't support any mode | |
1054 * here, hence an SQLWarning is generated if attempted to set | |
1055 * to true here. | |
1056 * | |
1057 * @param readOnly true enables read-only mode; false disables it | |
1058 * @throws SQLException if a database access error occurs or this | |
1059 * method is called during a transaction. | |
1060 */ | |
1061 @Override | |
1062 public void setReadOnly(final boolean readOnly) throws SQLException { | |
1063 if (readOnly == true) | |
1064 addWarning("cannot setReadOnly(true): read-only Connection mode not supported", "01M08"); | |
1065 } | |
1066 | |
1067 /** | |
1068 * Creates an unnamed savepoint in the current transaction and | |
1069 * returns the new Savepoint object that represents it. | |
1070 * | |
1071 * @return the new Savepoint object | |
1072 * @throws SQLException if a database access error occurs or this Connection | |
1073 * object is currently in auto-commit mode | |
1074 */ | |
1075 @Override | |
1076 public Savepoint setSavepoint() throws SQLException { | |
1077 checkNotClosed(); | |
1078 // create a new Savepoint object | |
1079 final MonetSavepoint sp = new MonetSavepoint(); | |
1080 | |
1081 // note: can't use sendIndependentCommand here because we need | |
1082 // to process the auto_commit state the server gives | |
1083 sendTransactionCommand("SAVEPOINT " + sp.getName()); | |
1084 return sp; | |
1085 } | |
1086 | |
1087 /** | |
1088 * Creates a savepoint with the given name in the current transaction | |
1089 * and returns the new Savepoint object that represents it. | |
1090 * | |
1091 * @param name a String containing the name of the savepoint | |
1092 * @return the new Savepoint object | |
1093 * @throws SQLException if a database access error occurs or this Connection | |
1094 * object is currently in auto-commit mode | |
1095 */ | |
1096 @Override | |
1097 public Savepoint setSavepoint(final String name) throws SQLException { | |
1098 checkNotClosed(); | |
1099 // create a new Savepoint object | |
1100 final MonetSavepoint sp; | |
1101 try { | |
1102 sp = new MonetSavepoint(name); | |
1103 } catch (IllegalArgumentException e) { | |
1104 throw new SQLException(e.getMessage(), "M0M03"); | |
1105 } | |
1106 | |
1107 // note: can't use sendIndependentCommand here because we need | |
1108 // to process the auto_commit state the server gives | |
1109 sendTransactionCommand("SAVEPOINT " + sp.getName()); | |
1110 return sp; | |
1111 } | |
1112 | |
1113 /** | |
1114 * Attempts to change the transaction isolation level for this | |
1115 * Connection object to the one given. The constants defined in the | |
1116 * interface Connection are the possible transaction isolation levels. | |
1117 * | |
1118 * @param level one of the following Connection constants: | |
1119 * Connection.TRANSACTION_READ_UNCOMMITTED, | |
1120 * Connection.TRANSACTION_READ_COMMITTED, | |
1121 * Connection.TRANSACTION_REPEATABLE_READ, or | |
1122 * Connection.TRANSACTION_SERIALIZABLE. | |
1123 */ | |
1124 @Override | |
1125 public void setTransactionIsolation(final int level) { | |
1126 if (level != TRANSACTION_SERIALIZABLE) { | |
1127 addWarning("MonetDB only supports fully serializable " + | |
1128 "transactions, continuing with transaction level " + | |
1129 "raised to TRANSACTION_SERIALIZABLE", "01M09"); | |
1130 } | |
1131 } | |
1132 | |
1133 /** | |
1134 * Installs the given TypeMap object as the type map for this | |
1135 * Connection object. The type map will be used for the custom | |
1136 * mapping of SQL structured types and distinct types. | |
1137 * | |
1138 * @param map the java.util.Map object to install as the replacement for | |
1139 * this Connection object's default type map | |
1140 */ | |
1141 @Override | |
1142 public void setTypeMap(final Map<String, Class<?>> map) { | |
1143 typeMap = map; | |
1144 } | |
1145 | |
1146 /** | |
1147 * Returns a string identifying this Connection to the MonetDB server. | |
1148 * | |
1149 * @return a String representing this Object | |
1150 */ | |
1151 @Override | |
1152 public String toString() { | |
1153 return "MonetDB Connection (" + getJDBCURL() + ") " + | |
1154 (closed ? "disconnected" : "connected"); | |
1155 } | |
1156 | |
1157 //== Java 1.6 methods (JDBC 4.0) | |
1158 | |
1159 /** | |
1160 * Factory method for creating Array objects. | |
1161 * | |
1162 * Note: When createArrayOf is used to create an array object that | |
1163 * maps to a primitive data type, then it is implementation-defined | |
1164 * whether the Array object is an array of that primitive data type | |
1165 * or an array of Object. | |
1166 * | |
1167 * Note: The JDBC driver is responsible for mapping the elements | |
1168 * Object array to the default JDBC SQL type defined in | |
1169 * java.sql.Types for the given class of Object. The default mapping | |
1170 * is specified in Appendix B of the JDBC specification. If the | |
1171 * resulting JDBC type is not the appropriate type for the given | |
1172 * typeName then it is implementation defined whether an | |
1173 * SQLException is thrown or the driver supports the resulting conversion. | |
1174 * | |
1175 * @param typeName the SQL name of the type the elements of the | |
1176 * array map to. The typeName is a database-specific name | |
1177 * which may be the name of a built-in type, a user-defined | |
1178 * type or a standard SQL type supported by this database. | |
1179 * This is the value returned by Array.getBaseTypeName | |
1180 * @return an Array object whose elements map to the specified SQL type | |
1181 * @throws SQLException - if a database error occurs, the JDBC type is not appropriate for the typeName and | |
1182 * the conversion is not supported, the typeName is null or this method is called on a closed connection | |
1183 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type | |
1184 * @since 1.6 | |
1185 */ | |
1186 @Override | |
1187 public java.sql.Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { | |
1188 throw newSQLFeatureNotSupportedException("createArrayOf"); | |
1189 } | |
1190 | |
1191 | |
1192 /** | |
1193 * Constructs an object that implements the Clob interface. The | |
1194 * object returned initially contains no data. The setAsciiStream, | |
1195 * setCharacterStream and setString methods of the Clob interface | |
1196 * may be used to add data to the Clob. | |
1197 * | |
1198 * @return a MonetClob instance | |
1199 * @throws SQLException - if an object that implements the Clob interface can not be constructed, | |
1200 * this method is called on a closed connection or a database access error occurs. | |
1201 * @since 1.6 | |
1202 */ | |
1203 @Override | |
1204 public java.sql.Clob createClob() throws SQLException { | |
1205 return new MonetClob(""); | |
1206 } | |
1207 | |
1208 /** | |
1209 * Constructs an object that implements the Blob interface. The | |
1210 * object returned initially contains no data. The setBinaryStream | |
1211 * and setBytes methods of the Blob interface may be used to add | |
1212 * data to the Blob. | |
1213 * | |
1214 * @return a MonetBlob instance | |
1215 * @throws SQLException - if an object that implements the Blob interface can not be constructed, | |
1216 * this method is called on a closed connection or a database access error occurs. | |
1217 * @since 1.6 | |
1218 */ | |
1219 @Override | |
1220 public java.sql.Blob createBlob() throws SQLException { | |
1221 return new MonetBlob(new byte[1]); | |
1222 } | |
1223 | |
1224 /** | |
1225 * Constructs an object that implements the NClob interface. The | |
1226 * object returned initially contains no data. The setAsciiStream, | |
1227 * setCharacterStream and setString methods of the NClob interface | |
1228 * may be used to add data to the NClob. | |
1229 * | |
1230 * @return an NClob instance | |
1231 * @throws SQLException - if an object that implements the NClob interface can not be constructed, | |
1232 * this method is called on a closed connection or a database access error occurs. | |
1233 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type | |
1234 * @since 1.6 | |
1235 */ | |
1236 @Override | |
1237 public java.sql.NClob createNClob() throws SQLException { | |
1238 throw newSQLFeatureNotSupportedException("createNClob"); | |
1239 } | |
1240 | |
1241 /** | |
1242 * Factory method for creating Struct objects. | |
1243 * | |
1244 * @param typeName the SQL type name of the SQL structured type that | |
1245 * this Struct object maps to. The typeName is the name of a | |
1246 * user-defined type that has been defined for this database. | |
1247 * It is the value returned by Struct.getSQLTypeName. | |
1248 * @param attributes the attributes that populate the returned object | |
1249 * @return a Struct object that maps to the given SQL type and is | |
1250 * populated with the given attributes | |
1251 * @throws SQLException - if an object that implements the Struct interface can not be constructed, | |
1252 * this method is called on a closed connection or a database access error occurs. | |
1253 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type | |
1254 * @since 1.6 | |
1255 */ | |
1256 @Override | |
1257 public java.sql.Struct createStruct(String typeName, Object[] attributes) throws SQLException { | |
1258 throw newSQLFeatureNotSupportedException("createStruct"); | |
1259 } | |
1260 | |
1261 /** | |
1262 * Constructs an object that implements the SQLXML interface. The | |
1263 * object returned initially contains no data. The | |
1264 * createXmlStreamWriter object and setString method of the SQLXML | |
1265 * interface may be used to add data to the SQLXML object. | |
1266 * | |
1267 * @return An object that implements the SQLXML interface | |
1268 * @throws SQLException - if an object that implements the SQLXML interface can not be constructed, | |
1269 * this method is called on a closed connection or a database access error occurs. | |
1270 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type | |
1271 * @since 1.6 | |
1272 */ | |
1273 @Override | |
1274 public java.sql.SQLXML createSQLXML() throws SQLException { | |
1275 throw newSQLFeatureNotSupportedException("createSQLXML"); | |
1276 } | |
1277 | |
1278 /** | |
1279 * Returns true if the connection has not been closed and is still | |
1280 * valid. The driver shall submit a query on the connection or use | |
1281 * some other mechanism that positively verifies the connection is | |
1282 * still valid when this method is called. | |
1283 * | |
1284 * The query submitted by the driver to validate the connection | |
1285 * shall be executed in the context of the current transaction. | |
1286 * | |
1287 * @param timeout The time in seconds to wait for the database | |
1288 * operation used to validate the connection to complete. If | |
1289 * the timeout period expires before the operation completes, | |
1290 * this method returns false. A value of 0 indicates a | |
1291 * timeout is not applied to the database operation. | |
1292 * @return true if the connection is valid, false otherwise | |
1293 * @throws SQLException if the value supplied for timeout is less than 0 | |
1294 * @since 1.6 | |
1295 */ | |
1296 @Override | |
1297 public boolean isValid(final int timeout) throws SQLException { | |
1298 if (timeout < 0) | |
1299 throw new SQLException("timeout is less than 0", "M1M05"); | |
1300 if (closed) | |
1301 return false; | |
1302 | |
1303 // ping monetdb server using query: select 1; | |
1304 Statement stmt = null; | |
1305 ResultSet rs = null; | |
1306 boolean isValid = false; | |
1307 final int original_timeout = this.lastSetQueryTimeout; | |
1308 try { | |
1309 stmt = createStatement(); | |
1310 if (stmt != null) { | |
1311 if (timeout > 0 && original_timeout != timeout) { | |
1312 // we need to change the requested timeout for this test query | |
1313 stmt.setQueryTimeout(timeout); | |
1314 } | |
1315 rs = stmt.executeQuery("SELECT 1"); | |
1316 if (rs != null && rs.next()) { | |
1317 isValid = true; | |
1318 } | |
1319 } | |
1320 } catch (SQLException se) { | |
1321 String msg = se.getMessage(); | |
1322 // System.out.println(se.getSQLState() + " Con.isValid(): " + msg); | |
1323 if (msg != null && msg.equalsIgnoreCase("Current transaction is aborted (please ROLLBACK)")) { | |
1324 // Must use equalsIgnoreCase() here because up to Jul2017 release 'Current' was 'current' so with lowercase c. | |
1325 // It changed to 'Current' after Jul2017 release. We need to support all server versions. | |
1326 // SQLState = 25005 | |
1327 isValid = true; | |
1328 } | |
1329 /* ignore stmt errors/exceptions, we are only testing if the connection is still alive and usable */ | |
1330 } finally { | |
1331 /* when changed, reset the original server timeout value on the server */ | |
1332 if (timeout > 0 && original_timeout != this.lastSetQueryTimeout) { | |
1333 this.lastSetQueryTimeout = original_timeout; | |
1334 Statement stmt2 = null; | |
1335 try { | |
1336 /* we have to set in the server explicitly, because the test 'queryTimeout != connection.lastSetQueryTimeout' | |
1337 on MonetStatement.internalExecute(sql) won't pass and the server won't be set back */ | |
1338 stmt2 = this.createStatement(); | |
1339 stmt2.execute("CALL \"sys\".\"settimeout\"(" + this.lastSetQueryTimeout + ")"); | |
1340 } catch (SQLException se) { | |
1341 /* ignore stmt errors/exceptions, we are only testing if the connection is still alive and usable */ | |
1342 } finally { | |
1343 closeResultsetStatement(null, stmt2); | |
1344 } | |
1345 } | |
1346 closeResultsetStatement(rs, stmt); | |
1347 } | |
1348 return isValid; | |
1349 } | |
1350 | |
1351 /** | |
1352 * Returns the value of the client info property specified by name. | |
1353 * This method may return null if the specified client info property | |
1354 * has not been set and does not have a default value. | |
1355 * This method will also return null if the specified client info | |
1356 * property name is not supported by the driver. | |
1357 * Applications may use the DatabaseMetaData.getClientInfoProperties method | |
1358 * to determine the client info properties supported by the driver. | |
1359 * | |
1360 * @param name - The name of the client info property to retrieve | |
1361 * @return The value of the client info property specified or null | |
1362 * @throws SQLException - if the database server returns an error | |
1363 * when fetching the client info value from the database | |
1364 * or this method is called on a closed connection | |
1365 * @since 1.6 | |
1366 */ | |
1367 @Override | |
1368 public String getClientInfo(final String name) throws SQLException { | |
1369 if (name == null || name.isEmpty()) | |
1370 return null; | |
1371 checkNotClosed(); | |
1372 return conn_props.getProperty(name); | |
1373 } | |
1374 | |
1375 /** | |
1376 * Returns a list containing the name and current value of each client info | |
1377 * property supported by the driver. The value of a client info property may | |
1378 * be null if the property has not been set and does not have a default value. | |
1379 * | |
1380 * @return A Properties object that contains the name and current value | |
1381 * of each of the client info properties supported by the driver. | |
1382 * @throws SQLException - if the database server returns an error | |
1383 * when fetching the client info value from the database | |
1384 * or this method is called on a closed connection | |
1385 * @since 1.6 | |
1386 */ | |
1387 @Override | |
1388 public Properties getClientInfo() throws SQLException { | |
1389 checkNotClosed(); | |
1390 // return a clone of the connection properties object | |
1391 return new Properties(conn_props); | |
1392 } | |
1393 | |
1394 /** | |
1395 * Sets the value of the client info property specified by name to the value specified by value. | |
1396 * Applications may use the DatabaseMetaData.getClientInfoProperties method to determine | |
1397 * the client info properties supported by the driver and the maximum length that may be specified | |
1398 * for each property. | |
1399 * | |
1400 * The driver stores the value specified in a suitable location in the database. For example | |
1401 * in a special register, session parameter, or system table column. For efficiency the driver | |
1402 * may defer setting the value in the database until the next time a statement is executed | |
1403 * or prepared. Other than storing the client information in the appropriate place in the | |
1404 * database, these methods shall not alter the behavior of the connection in anyway. | |
1405 * The values supplied to these methods are used for accounting, diagnostics and debugging purposes only. | |
1406 * | |
1407 * The driver shall generate a warning if the client info name specified is not recognized by the driver. | |
1408 * | |
1409 * If the value specified to this method is greater than the maximum length for the property | |
1410 * the driver may either truncate the value and generate a warning or generate a SQLClientInfoException. | |
1411 * If the driver generates a SQLClientInfoException, the value specified was not set on the connection. | |
1412 * | |
1413 * The following are standard client info properties. Drivers are not required to support these | |
1414 * properties however if the driver supports a client info property that can be described by one | |
1415 * of the standard properties, the standard property name should be used. | |
1416 * | |
1417 * ApplicationName - The name of the application currently utilizing the connection | |
1418 * ClientUser - The name of the user that the application using the connection is performing work for. | |
1419 * This may not be the same as the user name that was used in establishing the connection. | |
1420 * ClientHostname - The hostname of the computer the application using the connection is running on. | |
1421 * | |
1422 * @param name - The name of the client info property to set | |
1423 * @param value - The value to set the client info property to. If the | |
1424 * value is null, the current value of the specified property is cleared. | |
1425 * @throws SQLClientInfoException - if the database server returns an error | |
1426 * while setting the clientInfo values on the database server | |
1427 * or this method is called on a closed connection | |
1428 * @since 1.6 | |
1429 */ | |
1430 @Override | |
1431 public void setClientInfo(final String name, final String value) throws SQLClientInfoException { | |
1432 if (name == null || name.isEmpty()) { | |
1433 addWarning("setClientInfo: missing property name", "01M07"); | |
1434 return; | |
1435 } | |
1436 // If the value is null, the current value of the specified property is cleared. | |
1437 if (value == null) { | |
1438 if (conn_props.containsKey(name)) | |
1439 conn_props.remove(name); | |
1440 return; | |
1441 } | |
1442 // only set value for supported property names | |
1443 if (name.equals("host") || | |
1444 name.equals("port") || | |
1445 name.equals("user") || | |
1446 name.equals("password") || | |
1447 name.equals("database") || | |
1448 name.equals("language") || | |
1449 name.equals("so_timeout") || | |
1450 name.equals("debug") || | |
1451 name.equals("hash") || | |
1452 name.equals("treat_blob_as_binary") || | |
1453 name.equals("treat_clob_as_varchar")) | |
1454 { | |
1455 conn_props.setProperty(name, value); | |
1456 } else { | |
1457 addWarning("setClientInfo: " + name + "is not a recognised property", "01M07"); | |
1458 } | |
1459 } | |
1460 | |
1461 /** | |
1462 * Sets the value of the connection's client info properties. | |
1463 * The Properties object contains the names and values of the client info | |
1464 * properties to be set. The set of client info properties contained in the | |
1465 * properties list replaces the current set of client info properties on the connection. | |
1466 * If a property that is currently set on the connection is not present in the | |
1467 * properties list, that property is cleared. Specifying an empty properties list | |
1468 * will clear all of the properties on the connection. | |
1469 * See setClientInfo (String, String) for more information. | |
1470 * | |
1471 * If an error occurs in setting any of the client info properties, a | |
1472 * SQLClientInfoException is thrown. The SQLClientInfoException contains information | |
1473 * indicating which client info properties were not set. The state of the client | |
1474 * information is unknown because some databases do not allow multiple client info | |
1475 * properties to be set atomically. For those databases, one or more properties may | |
1476 * have been set before the error occurred. | |
1477 * | |
1478 * @param props - The list of client info properties to set | |
1479 * @throws SQLClientInfoException - if the database server returns an error | |
1480 * while setting the clientInfo values on the database server | |
1481 * or this method is called on a closed connection | |
1482 * @since 1.6 | |
1483 */ | |
1484 @Override | |
1485 public void setClientInfo(final Properties props) throws SQLClientInfoException { | |
1486 if (props != null) { | |
1487 for (Entry<Object, Object> entry : props.entrySet()) { | |
1488 setClientInfo(entry.getKey().toString(), entry.getValue().toString()); | |
1489 } | |
1490 } | |
1491 } | |
1492 | |
1493 | |
1494 //== Java 1.7 methods (JDBC 4.1) | |
1495 | |
1496 /** | |
1497 * Sets the given schema name to access. | |
1498 * | |
1499 * @param schema the name of a schema in which to work | |
1500 * @throws SQLException if a database access error occurs or this | |
1501 * method is called on a closed connection | |
1502 * @since 1.7 | |
1503 */ | |
1504 @Override | |
1505 public void setSchema(final String schema) throws SQLException { | |
1506 checkNotClosed(); | |
1507 if (schema == null || schema.isEmpty()) | |
1508 throw new SQLException("Missing schema name", "M1M05"); | |
1509 | |
1510 Statement st = null; | |
1511 try { | |
1512 st = createStatement(); | |
1513 if (st != null) | |
1514 st.execute("SET SCHEMA \"" + schema + "\""); | |
1515 // do not catch any Exception, just let it propagate | |
1516 } finally { | |
1517 closeResultsetStatement(null, st); | |
1518 } | |
1519 } | |
1520 | |
1521 /** | |
1522 * Retrieves this Connection object's current schema name. | |
1523 * | |
1524 * @return the current schema name or null if there is none | |
1525 * @throws SQLException if a database access error occurs or this | |
1526 * method is called on a closed connection | |
1527 * @since 1.7 | |
1528 */ | |
1529 @Override | |
1530 public String getSchema() throws SQLException { | |
1531 checkNotClosed(); | |
1532 | |
1533 String cur_schema = null; | |
1534 Statement st = null; | |
1535 ResultSet rs = null; | |
1536 try { | |
1537 st = createStatement(); | |
1538 if (st != null) { | |
1539 rs = st.executeQuery("SELECT CURRENT_SCHEMA"); | |
1540 if (rs != null) { | |
1541 if (rs.next()) | |
1542 cur_schema = rs.getString(1); | |
1543 } | |
1544 } | |
1545 // do not catch any Exception, just let it propagate | |
1546 } finally { | |
1547 closeResultsetStatement(rs, st); | |
1548 } | |
1549 if (cur_schema == null) | |
1550 throw new SQLException("Failed to fetch schema name", "02000"); | |
1551 return cur_schema; | |
1552 } | |
1553 | |
1554 /** | |
1555 * Terminates an open connection. Calling abort results in: | |
1556 * * The connection marked as closed | |
1557 * * Closes any physical connection to the database | |
1558 * * Releases resources used by the connection | |
1559 * * Insures that any thread that is currently accessing the | |
1560 * connection will either progress to completion or throw an | |
1561 * SQLException. | |
1562 * Calling abort marks the connection closed and releases any | |
1563 * resources. Calling abort on a closed connection is a no-op. | |
1564 * | |
1565 * @param executor The Executor implementation which will be used by | |
1566 * abort | |
1567 * @throws SQLException if a database access error occurs or the | |
1568 * executor is null | |
1569 * @throws SecurityException if a security manager exists and | |
1570 * its checkPermission method denies calling abort | |
1571 * @since 1.7 | |
1572 */ | |
1573 @Override | |
1574 public void abort(final Executor executor) throws SQLException { | |
1575 if (closed) | |
1576 return; | |
1577 if (executor == null) | |
1578 throw new SQLException("executor is null", "M1M05"); | |
1579 // this is really the simplest thing to do, it destroys | |
1580 // everything (in particular the server connection) | |
1581 close(); | |
1582 } | |
1583 | |
1584 /** | |
1585 * Sets the maximum period a Connection or objects created from the | |
1586 * Connection will wait for the database to reply to any one | |
1587 * request. If any request remains unanswered, the waiting method | |
1588 * will return with a SQLException, and the Connection or objects | |
1589 * created from the Connection will be marked as closed. Any | |
1590 * subsequent use of the objects, with the exception of the close, | |
1591 * isClosed or Connection.isValid methods, will result in a | |
1592 * SQLException. | |
1593 * | |
1594 * @param executor The Executor implementation which will be used by | |
1595 * setNetworkTimeout | |
1596 * @param millis The time in milliseconds to wait for the | |
1597 * database operation to complete | |
1598 * @throws SQLException if a database access error occurs, this | |
1599 * method is called on a closed connection, the executor is | |
1600 * null, or the value specified for seconds is less than 0. | |
1601 * @since 1.7 | |
1602 */ | |
1603 @Override | |
1604 public void setNetworkTimeout(final Executor executor, final int millis) throws SQLException { | |
1605 checkNotClosed(); | |
1606 // executor object is not used yet, so no need to test it. | |
1607 // if (executor == null) | |
1608 // throw new SQLException("executor is null", "M1M05"); | |
1609 if (millis < 0) | |
1610 throw new SQLException("milliseconds is less than zero", "M1M05"); | |
1611 | |
1612 try { | |
1613 server.setSoTimeout(millis); | |
1614 } catch (SocketException e) { | |
1615 throw new SQLNonTransientConnectionException(e.getMessage(), "08000"); | |
1616 } | |
1617 } | |
1618 | |
1619 /** | |
1620 * Retrieves the number of milliseconds the driver will wait for a | |
1621 * database request to complete. If the limit is exceeded, a | |
1622 * SQLException is thrown. | |
1623 * | |
1624 * @return the current timeout limit in milliseconds; zero means | |
1625 * there is no limit | |
1626 * @throws SQLException if a database access error occurs or | |
1627 * this method is called on a closed Connection | |
1628 * @since 1.7 | |
1629 */ | |
1630 @Override | |
1631 public int getNetworkTimeout() throws SQLException { | |
1632 checkNotClosed(); | |
1633 try { | |
1634 return server.getSoTimeout(); | |
1635 } catch (SocketException e) { | |
1636 throw new SQLNonTransientConnectionException(e.getMessage(), "08000"); | |
1637 } | |
1638 } | |
1639 | |
1640 //== end methods of interface java.sql.Connection | |
1641 | |
1642 | |
1643 //== internal helper methods which do not belong to the JDBC interface | |
1644 | |
1645 /** | |
1646 * @return whether the JDBC BLOB type should be mapped to VARBINARY type. | |
1647 * This allows generic JDBC programs to fetch Blob data via getBytes() | |
1648 * instead of getBlob() and Blob.getBinaryStream() to reduce overhead. | |
1649 * It is called from: MonetResultSet and MonetPreparedStatement | |
1650 */ | |
1651 boolean mapBlobAsVarBinary() { | |
1652 return treatBlobAsVarBinary; | |
1653 } | |
1654 | |
1655 /** | |
1656 * @return whether the JDBC CLOB type should be mapped to VARCHAR type. | |
1657 * This allows generic JDBC programs to fetch Clob data via getString() | |
1658 * instead of getClob() and Clob.getCharacterStream() to reduce overhead. | |
1659 * It is called from: MonetResultSet and MonetPreparedStatement | |
1660 */ | |
1661 boolean mapClobAsVarChar() { | |
1662 return treatClobAsVarChar; | |
1663 } | |
1664 | |
1665 /** | |
1666 * Local helper method to test whether the Connection object is closed | |
1667 * When closed it throws an SQLException | |
1668 */ | |
1669 private void checkNotClosed() throws SQLException { | |
1670 if (closed) | |
1671 throw new SQLException("Connection is closed", "M1M20"); | |
1672 } | |
1673 | |
1674 /** | |
1675 * @return the MonetDB JDBC Connection URL (without user name and password). | |
1676 * It is called from: getURL()in MonetDatabaseMetaData | |
1677 */ | |
1678 String getJDBCURL() { | |
1679 final StringBuilder sb = new StringBuilder(128); | |
1680 sb.append(MonetDriver.MONETURL).append(hostname) | |
1681 .append(':').append(port) | |
1682 .append('/').append(database); | |
1683 if (lang == LANG_MAL) | |
1684 sb.append("?language=mal"); | |
1685 return sb.toString(); | |
1686 } | |
1687 | |
1688 /** | |
1689 * Utility method to escape all ocurrences of special characters | |
1690 * (double slashes and single quotes) in a string literal | |
1691 * It is called from: MonetDatabaseMetaData and MonetPreparedStatement | |
1692 */ | |
1693 final String escapeSpecialChars(final String in) { | |
1694 String ret = in; | |
1695 if (ret.contains("\\\\")) | |
1696 // all double slashes in input need to be escaped. | |
1697 ret = ret.replaceAll("\\\\", "\\\\\\\\"); | |
1698 if (ret.contains("'")) | |
1699 // all single quotes in input need to be escaped. | |
1700 ret = ret.replaceAll("'", "\\\\'"); | |
1701 return ret; | |
1702 } | |
1703 | |
1704 | |
1705 // Internal cache for 3 static mserver environment values, so they aren't queried from mserver again and again | |
1706 private String env_current_user = null; | |
1707 private String env_monet_version = null; | |
1708 private String env_max_clients = null; | |
1709 | |
1710 /** | |
1711 * Utility method to fetch 3 mserver environment values combined in one query for efficiency. | |
1712 * We currently fetch the env values of: current_user, monet_version and max_clients. | |
1713 * We cache them such that we do not need to query the server again and again. | |
1714 */ | |
1715 private synchronized void getEnvValues() throws SQLException { | |
1716 Statement st = null; | |
1717 ResultSet rs = null; | |
1718 try { | |
1719 st = createStatement(); | |
1720 if (st != null) { | |
1721 rs = st.executeQuery( | |
1722 "SELECT \"name\", \"value\" FROM \"sys\".\"env\"()" + | |
1723 " WHERE \"name\" IN ('monet_version', 'max_clients')" + | |
1724 " UNION SELECT 'current_user' as \"name\", current_user as \"value\""); | |
1725 if (rs != null) { | |
1726 while (rs.next()) { | |
1727 final String prop = rs.getString(1); | |
1728 final String value = rs.getString(2); | |
1729 if ("current_user".equals(prop)) { | |
1730 env_current_user = value; | |
1731 } else | |
1732 if ("monet_version".equals(prop)) { | |
1733 env_monet_version = value; | |
1734 } else | |
1735 if ("max_clients".equals(prop)) { | |
1736 env_max_clients = value; | |
1737 } | |
1738 } | |
1739 } | |
1740 } | |
1741 /* do not catch SQLException here, as we need to know it when it fails */ | |
1742 } finally { | |
1743 closeResultsetStatement(rs, st); | |
1744 } | |
1745 // for debug: System.out.println("Read: env_current_user: " + env_current_user + " env_monet_version: " + env_monet_version + " env_max_clients: " + env_max_clients); | |
1746 } | |
1747 | |
1748 /** | |
1749 * @return the current User Name. | |
1750 * It is called from: MonetDatabaseMetaData | |
1751 */ | |
1752 String getUserName() throws SQLException { | |
1753 if (env_current_user == null) | |
1754 getEnvValues(); | |
1755 return env_current_user; | |
1756 } | |
1757 | |
1758 /** | |
1759 * @return the MonetDB Database Server version string. | |
1760 * It is called from: MonetDatabaseMetaData | |
1761 */ | |
1762 String getDatabaseProductVersion() throws SQLException { | |
1763 if (env_monet_version == null) | |
1764 getEnvValues(); | |
1765 // always return a valid String to prevent NPE in getTables() and getTableTypes() | |
1766 if (env_monet_version != null) | |
1767 return env_monet_version; | |
1768 return ""; | |
1769 } | |
1770 | |
1771 /** | |
1772 * @return the MonetDB Database Server major version number. | |
1773 * It is called from: MonetDatabaseMetaData | |
1774 */ | |
1775 int getDatabaseMajorVersion() throws SQLException { | |
1776 if (env_monet_version == null) | |
1777 getEnvValues(); | |
1778 if (env_monet_version != null) { | |
1779 try { | |
1780 // from version string such as 11.33.9 extract number: 11 | |
1781 final int start = env_monet_version.indexOf('.'); | |
1782 return Integer.parseInt((start >= 0) ? env_monet_version.substring(0, start) : env_monet_version); | |
1783 } catch (NumberFormatException nfe) { | |
1784 // ignore | |
1785 } | |
1786 } | |
1787 return 0; | |
1788 } | |
1789 | |
1790 /** | |
1791 * @return the MonetDB Database Server minor version number. | |
1792 * It is called from: MonetDatabaseMetaData | |
1793 */ | |
1794 int getDatabaseMinorVersion() throws SQLException { | |
1795 if (env_monet_version == null) | |
1796 getEnvValues(); | |
1797 if (env_monet_version != null) { | |
1798 try { | |
1799 // from version string such as 11.33.9 extract number: 33 | |
1800 int start = env_monet_version.indexOf('.'); | |
1801 if (start >= 0) { | |
1802 start++; | |
1803 final int end = env_monet_version.indexOf('.', start); | |
1804 return Integer.parseInt((end > 0) ? env_monet_version.substring(start, end) : env_monet_version.substring(start)); | |
1805 } | |
1806 } catch (NumberFormatException nfe) { | |
1807 // ignore | |
1808 } | |
1809 } | |
1810 return 0; | |
1811 } | |
1812 | |
1813 /** | |
1814 * @return the maximum number of active connections possible at one time; | |
1815 * a result of zero means that there is no limit or the limit is not known | |
1816 * It is called from: MonetDatabaseMetaData | |
1817 */ | |
1818 int getMaxConnections() throws SQLException { | |
1819 if (env_max_clients == null) | |
1820 getEnvValues(); | |
1821 if (env_max_clients != null) { | |
1822 try { | |
1823 return Integer.parseInt(env_max_clients); | |
1824 } catch (NumberFormatException nfe) { | |
1825 /* ignore */ | |
1826 } | |
1827 } | |
1828 return 0; | |
1829 } | |
1830 | |
1831 | |
1832 // Internal cache for determining if system table sys.privilege_codes (new as of Jul2017 release) exists on connected server | |
1833 private boolean queriedPrivilege_codesTable = false; | |
1834 private boolean hasPrivilege_codesTable = false; | |
1835 /** | |
1836 * Utility method to query the server to find out if it has | |
1837 * the system table sys.privilege_codes (which is new as of Jul2017 release). | |
1838 * The result is cached and reused, so that we only test the query once per connection. | |
1839 * This method is used by methods from MonetDatabaseMetaData. | |
1840 */ | |
1841 boolean privilege_codesTableExists() { | |
1842 if (!queriedPrivilege_codesTable) { | |
1843 hasPrivilege_codesTable = existsSysTable("privilege_codes"); | |
1844 queriedPrivilege_codesTable = true; // set flag, so the querying is done only at first invocation. | |
1845 } | |
1846 return hasPrivilege_codesTable; | |
1847 } | |
1848 | |
1849 // Internal cache for determining if system table sys.comments (new as of Mar2018 release) exists on connected server | |
1850 private boolean queriedCommentsTable = false; | |
1851 private boolean hasCommentsTable = false; | |
1852 /** | |
1853 * Utility method to query the server to find out if it has | |
1854 * the system table sys.comments (which is new as of Mar2018 release). | |
1855 * The result is cached and reused, so that we only test the query once per connection. | |
1856 * This method is used by methods from MonetDatabaseMetaData. | |
1857 */ | |
1858 boolean commentsTableExists() { | |
1859 if (!queriedCommentsTable) { | |
1860 hasCommentsTable = existsSysTable("comments"); | |
1861 queriedCommentsTable = true; // set flag, so the querying is done only at first invocation. | |
1862 } | |
1863 return hasCommentsTable; | |
1864 } | |
1865 | |
1866 | |
1867 /** | |
1868 * Internal utility method to query the server to find out if it has a specific system table sys.<tablename>. | |
1869 */ | |
1870 private boolean existsSysTable(final String tablename) { | |
1871 boolean exists = false; | |
1872 Statement stmt = null; | |
1873 ResultSet rs = null; | |
1874 try { | |
1875 stmt = createStatement(); | |
1876 if (stmt != null) { | |
1877 rs = stmt.executeQuery("SELECT id FROM sys._tables WHERE name = '" | |
1878 + tablename | |
1879 + "' AND schema_id IN (SELECT id FROM sys.schemas WHERE name = 'sys')"); | |
1880 if (rs != null) { | |
1881 exists = rs.next(); // if a row is available it exists, else not | |
1882 } | |
1883 } | |
1884 } catch (SQLException se) { | |
1885 /* ignore */ | |
1886 } finally { | |
1887 closeResultsetStatement(rs, stmt); | |
1888 } | |
1889 // for debug: System.out.println("testTableExists(" + tablename + ") returns: " + exists); | |
1890 return exists; | |
1891 } | |
1892 | |
1893 /** | |
1894 * Closes a ResultSet and/or Statement object without throwing any SQLExceptions | |
1895 * It can be used in the finally clause after creating a Statement and | |
1896 * (optionally) executed a query which produced a ResultSet. | |
1897 * | |
1898 * @param rs ResultSet object to be closed. It may be null | |
1899 * @param st Statement object to be closed. It may be null | |
1900 */ | |
1901 static final void closeResultsetStatement(final ResultSet rs, final Statement st) { | |
1902 if (rs != null) { | |
1903 try { | |
1904 rs.close(); | |
1905 } catch (SQLException e) { /* ignore */ } | |
1906 } | |
1907 if (st != null) { | |
1908 try { | |
1909 st.close(); | |
1910 } catch (SQLException e) { /* ignore */ } | |
1911 } | |
1912 } | |
1913 | |
1914 /** | |
1915 * Sends the given string to MonetDB as special transaction command. | |
1916 * All possible returned information is discarded. | |
1917 * Encountered errors are reported. | |
1918 * | |
1919 * @param command the exact string to send to MonetDB | |
1920 * @throws SQLException if an IO exception or a database error occurs | |
1921 */ | |
1922 private void sendTransactionCommand(final String command) throws SQLException { | |
1923 // create a container for the result | |
1924 final ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY); | |
1925 // send the appropriate query string to the database | |
1926 try { | |
1927 l.processQuery(command); | |
1928 } finally { | |
1929 l.close(); | |
1930 } | |
1931 } | |
1932 | |
1933 /** | |
1934 * Sends the given string to MonetDB as regular SQL statement/query using queryTempl | |
1935 * Making sure there is a prompt after the command is sent. All possible | |
1936 * returned information is discarded. Encountered errors are reported. | |
1937 * | |
1938 * @param command the exact string to send to MonetDB | |
1939 * @throws SQLException if an IO exception or a database error occurs | |
1940 */ | |
1941 private void sendIndependentCommand(String command) throws SQLException { | |
1942 sendCommand(command, true); | |
1943 } | |
1944 | |
1945 /** | |
1946 * Sends the given string to MonetDB as control statement using commandTempl | |
1947 * Making sure there is a prompt after the command is sent. All possible | |
1948 * returned information is discarded. Encountered errors are reported. | |
1949 * | |
1950 * @param command the exact string to send to MonetDB | |
1951 * @throws SQLException if an IO exception or a database error occurs | |
1952 */ | |
1953 void sendControlCommand(String command) throws SQLException { | |
1954 // send X command | |
1955 sendCommand(command, false); | |
1956 } | |
1957 | |
1958 /** | |
1959 * Sends the given string to MonetDB as command/query using commandTempl or queryTempl | |
1960 * Making sure there is a prompt after the command is sent. All possible | |
1961 * returned information is discarded. Encountered errors are reported. | |
1962 * | |
1963 * @param command the exact string to send to MonetDB | |
1964 * @param usequeryTempl send the command using queryTempl or else using commandTempl | |
1965 * @throws SQLException if an IO exception or a database error occurs | |
1966 */ | |
1967 private void sendCommand(final String command, final boolean usequeryTempl) throws SQLException { | |
1968 synchronized (server) { | |
1969 try { | |
1970 out.writeLine(usequeryTempl ? (queryTempl[0] + command + queryTempl[1]) | |
1971 : (commandTempl[0] + command + commandTempl[1]) ); | |
1972 final String error = in.waitForPrompt(); | |
1973 if (error != null) | |
1974 throw new SQLException(error.substring(6), error.substring(0, 5)); | |
1975 } catch (SocketTimeoutException e) { | |
1976 close(); // JDBC 4.1 semantics, abort() | |
1977 throw new SQLNonTransientConnectionException("connection timed out", "08M33"); | |
1978 } catch (IOException e) { | |
1979 throw new SQLNonTransientConnectionException(e.getMessage(), "08000"); | |
1980 } | |
1981 } | |
1982 } | |
1983 | |
1984 /** | |
1985 * Adds a warning to the pile of warnings this Connection object | |
1986 * has. If there were no warnings (or clearWarnings was called) | |
1987 * this warning will be the first, otherwise this warning will get | |
1988 * appended to the current warning. | |
1989 * | |
1990 * @param reason the warning message | |
1991 */ | |
1992 private final void addWarning(final String reason, final String sqlstate) { | |
1993 final SQLWarning warng = new SQLWarning(reason, sqlstate); | |
1994 if (warnings == null) { | |
1995 warnings = warng; | |
1996 } else { | |
1997 warnings.setNextWarning(warng); | |
1998 } | |
1999 } | |
2000 | |
2001 /** the default number of rows that are (attempted to) read at once */ | |
2002 private static final int DEF_FETCHSIZE = 250; | |
2003 /** The sequence counter */ | |
2004 private static int seqCounter = 0; | |
2005 | |
2006 /** | |
2007 * A Response is a message sent by the server to indicate some | |
2008 * action has taken place, and possible results of that action. | |
2009 */ | |
2010 // {{{ interface Response | |
2011 interface Response { | |
2012 /** | |
2013 * Adds a line to the underlying Response implementation. | |
2014 * | |
2015 * @param line the header line as String | |
2016 * @param linetype the line type according to the MAPI protocol | |
2017 * @return a non-null String if the line is invalid, | |
2018 * or additional lines are not allowed. | |
2019 */ | |
2020 public abstract String addLine(String line, int linetype); | |
2021 | |
2022 /** | |
2023 * Returns whether this Response expects more lines to be added | |
2024 * to it. | |
2025 * | |
2026 * @return true if a next line should be added, false otherwise | |
2027 */ | |
2028 public abstract boolean wantsMore(); | |
2029 | |
2030 /** | |
2031 * Indicates that no more header lines will be added to this | |
2032 * Response implementation. | |
2033 * | |
2034 * @throws SQLException if the contents of the Response is not | |
2035 * consistent or sufficient. | |
2036 */ | |
2037 /* MvD: disabled not used/needed code | |
2038 public abstract void complete() throws SQLException; | |
2039 */ | |
2040 | |
2041 /** | |
2042 * Instructs the Response implementation to close and do the | |
2043 * necessary clean up procedures. | |
2044 */ | |
2045 public abstract void close(); | |
2046 } | |
2047 // }}} | |
2048 | |
2049 /** | |
2050 * The ResultSetResponse represents a tabular result sent by the | |
2051 * server. This is typically an SQL table. The MAPI headers of the | |
2052 * Response look like: | |
2053 * <pre> | |
2054 * &1 1 28 2 10 | |
2055 * # name, value # name | |
2056 * # varchar, varchar # type | |
2057 * </pre> | |
2058 * there the first line consists out of<br /> | |
2059 * <tt>&"qt" "id" "tc" "cc" "rc"</tt>. | |
2060 */ | |
2061 // {{{ ResultSetResponse class implementation | |
2062 final class ResultSetResponse implements Response { | |
2063 /** The number of columns in this result */ | |
2064 public final int columncount; | |
2065 /** The total number of rows this result set has */ | |
2066 public final long tuplecount; | |
2067 /** The numbers of rows to retrieve per DataBlockResponse */ | |
2068 private int cacheSize; | |
2069 /** The table ID of this result */ | |
2070 public final int id; | |
2071 /** The names of the columns in this result */ | |
2072 private String[] name; | |
2073 /** The types of the columns in this result */ | |
2074 private String[] type; | |
2075 /** The max string length for each column in this result */ | |
2076 private int[] columnLengths; | |
2077 /** The table for each column in this result */ | |
2078 private String[] tableNames; | |
2079 /** The query sequence number */ | |
2080 private final int seqnr; | |
2081 /** A List of result blocks (chunks of size fetchSize/cacheSize) */ | |
2082 private DataBlockResponse[] resultBlocks; | |
2083 | |
2084 /** Whether this Response is closed */ | |
2085 private boolean closed; | |
2086 | |
2087 /** The Connection that we should use when requesting a new block */ | |
2088 private final MonetConnection.ResponseList parent; | |
2089 /** Whether the fetchSize was explitly set by the user */ | |
2090 private final boolean cacheSizeSetExplicitly; | |
2091 /** Whether we should send an Xclose command to the server | |
2092 * if we close this Response */ | |
2093 private boolean destroyOnClose; | |
2094 /** the offset to be used on Xexport queries */ | |
2095 private int blockOffset = 0; | |
2096 | |
2097 /** A parser for header lines */ | |
2098 private final HeaderLineParser hlp; | |
2099 | |
2100 /** A boolean array telling whether the headers are set or not */ | |
2101 private final boolean[] isSet; | |
2102 private static final int NAMES = 0; | |
2103 private static final int TYPES = 1; | |
2104 private static final int TABLES = 2; | |
2105 private static final int LENS = 3; | |
2106 | |
2107 | |
2108 /** | |
2109 * Sole constructor, which requires a MonetConnection parent to | |
2110 * be given. | |
2111 * | |
2112 * @param id the ID of the result set | |
2113 * @param tuplecount the total number of tuples in the result set | |
2114 * @param columncount the number of columns in the result set | |
2115 * @param rowcount the number of rows in the current block | |
2116 * @param parent the parent that created this Response and will | |
2117 * supply new result blocks when necessary | |
2118 * @param seq the query sequence number | |
2119 */ | |
2120 ResultSetResponse( | |
2121 final int id, | |
2122 final long tuplecount, | |
2123 final int columncount, | |
2124 final int rowcount, | |
2125 final MonetConnection.ResponseList parent, | |
2126 final int seq) | |
2127 throws SQLException | |
2128 { | |
2129 isSet = new boolean[4]; | |
2130 this.parent = parent; | |
2131 if (parent.cachesize == 0) { | |
2132 /* Below we have to calculate how many "chunks" we need | |
2133 * to allocate to store the entire result. However, if | |
2134 * the user didn't set a cache size, as in this case, we | |
2135 * need to stick to our defaults. */ | |
2136 cacheSize = MonetConnection.DEF_FETCHSIZE; | |
2137 cacheSizeSetExplicitly = false; | |
2138 } else { | |
2139 cacheSize = parent.cachesize; | |
2140 cacheSizeSetExplicitly = true; | |
2141 } | |
2142 /* So far, so good. Now the problem with EXPLAIN, DOT, etc | |
2143 * queries is, that they don't support any block fetching, | |
2144 * so we need to always fetch everything at once. For that | |
2145 * reason, the cache size is here set to the rowcount if | |
2146 * it's larger, such that we do a full fetch at once. | |
2147 * (Because we always set a reply_size, we can only get a | |
2148 * larger rowcount from the server if it doesn't paginate, | |
2149 * because it's a pseudo SQL result.) */ | |
2150 if (rowcount > cacheSize) | |
2151 cacheSize = rowcount; | |
2152 seqnr = seq; | |
2153 closed = false; | |
2154 destroyOnClose = id > 0 && tuplecount > rowcount; | |
2155 | |
2156 this.id = id; | |
2157 this.tuplecount = tuplecount; | |
2158 this.columncount = columncount; | |
2159 this.resultBlocks = new DataBlockResponse[(int)(tuplecount / cacheSize) + 1]; | |
2160 | |
2161 hlp = new HeaderLineParser(columncount); | |
2162 | |
2163 resultBlocks[0] = new DataBlockResponse(rowcount, parent.rstype == ResultSet.TYPE_FORWARD_ONLY); | |
2164 } | |
2165 | |
2166 /** | |
2167 * Parses the given string and changes the value of the matching | |
2168 * header appropriately, or passes it on to the underlying | |
2169 * DataResponse. | |
2170 * | |
2171 * @param tmpLine the string that contains the header | |
2172 * @return a non-null String if the header cannot be parsed or | |
2173 * is unknown | |
2174 */ | |
2175 // {{{ addLine | |
2176 @Override | |
2177 public String addLine(final String tmpLine, final int linetype) { | |
2178 if (isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES]) { | |
2179 return resultBlocks[0].addLine(tmpLine, linetype); | |
2180 } | |
2181 | |
2182 if (linetype != BufferedMCLReader.HEADER) | |
2183 return "header expected, got: " + tmpLine; | |
2184 | |
2185 // depending on the name of the header, we continue | |
2186 try { | |
2187 switch (hlp.parse(tmpLine)) { | |
2188 case HeaderLineParser.NAME: | |
2189 name = hlp.values.clone(); | |
2190 isSet[NAMES] = true; | |
2191 break; | |
2192 case HeaderLineParser.LENGTH: | |
2193 columnLengths = hlp.intValues.clone(); | |
2194 isSet[LENS] = true; | |
2195 break; | |
2196 case HeaderLineParser.TYPE: | |
2197 type = hlp.values.clone(); | |
2198 isSet[TYPES] = true; | |
2199 break; | |
2200 case HeaderLineParser.TABLE: | |
2201 tableNames = hlp.values.clone(); | |
2202 isSet[TABLES] = true; | |
2203 break; | |
2204 } | |
2205 } catch (MCLParseException e) { | |
2206 return e.getMessage(); | |
2207 } | |
2208 | |
2209 // all is well | |
2210 return null; | |
2211 } | |
2212 // }}} | |
2213 | |
2214 /** | |
2215 * Returns whether this ResultSetResponse needs more lines. | |
2216 * This method returns true if not all headers are set, or the | |
2217 * first DataBlockResponse reports to want more. | |
2218 */ | |
2219 @Override | |
2220 public boolean wantsMore() { | |
2221 if (isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES]) { | |
2222 return resultBlocks[0].wantsMore(); | |
2223 } else { | |
2224 return true; | |
2225 } | |
2226 } | |
2227 | |
2228 /** | |
2229 * Returns an array of Strings containing the values between | |
2230 * ',\t' separators. | |
2231 * | |
2232 * @param chrLine a character array holding the input data | |
2233 * @param start where the relevant data starts | |
2234 * @param stop where the relevant data stops | |
2235 * @return an array of Strings | |
2236 */ | |
2237 private final String[] getValues(final char[] chrLine, int start, final int stop) { | |
2238 int elem = 0; | |
2239 final String[] values = new String[columncount]; | |
2240 | |
2241 for (int i = start; i < stop; i++) { | |
2242 if (chrLine[i] == '\t' && chrLine[i - 1] == ',') { | |
2243 values[elem++] = | |
2244 new String(chrLine, start, i - 1 - start); | |
2245 start = i + 1; | |
2246 } | |
2247 } | |
2248 // at the left over part | |
2249 values[elem++] = new String(chrLine, start, stop - start); | |
2250 | |
2251 return values; | |
2252 } | |
2253 | |
2254 /** | |
2255 * Adds the given DataBlockResponse to this ResultSetResponse at | |
2256 * the given block position. | |
2257 * | |
2258 * @param offset the offset number of rows for this block | |
2259 * @param rr the DataBlockResponse to add | |
2260 */ | |
2261 void addDataBlockResponse(final int offset, final DataBlockResponse rr) { | |
2262 final int block = (offset - blockOffset) / cacheSize; | |
2263 resultBlocks[block] = rr; | |
2264 } | |
2265 | |
2266 /** | |
2267 * Marks this Response as being completed. A complete Response | |
2268 * needs to be consistent with regard to its internal data. | |
2269 * | |
2270 * @throws SQLException if the data currently in this Response is not | |
2271 * sufficient to be consistant | |
2272 */ | |
2273 /* MvD: disabled not used/needed code | |
2274 @Override | |
2275 public void complete() throws SQLException { | |
2276 final StringBuilder err = new StringBuilder(99); | |
2277 if (!isSet[NAMES]) err.append("name header missing\n"); | |
2278 if (!isSet[TYPES]) err.append("type header missing\n"); | |
2279 if (!isSet[TABLES]) err.append("table name header missing\n"); | |
2280 if (!isSet[LENS]) err.append("column width header missing\n"); | |
2281 if (err.length() > 0) | |
2282 throw new SQLException(err.toString(), "M0M10"); | |
2283 } | |
2284 */ | |
2285 | |
2286 /** | |
2287 * Returns the names of the columns | |
2288 * | |
2289 * @return the names of the columns | |
2290 */ | |
2291 String[] getNames() { | |
2292 return name; | |
2293 } | |
2294 | |
2295 /** | |
2296 * Returns the types of the columns | |
2297 * | |
2298 * @return the types of the columns | |
2299 */ | |
2300 String[] getTypes() { | |
2301 return type; | |
2302 } | |
2303 | |
2304 /** | |
2305 * Returns the tables of the columns | |
2306 * | |
2307 * @return the tables of the columns | |
2308 */ | |
2309 String[] getTableNames() { | |
2310 return tableNames; | |
2311 } | |
2312 | |
2313 /** | |
2314 * Returns the lengths of the columns | |
2315 * | |
2316 * @return the lengths of the columns | |
2317 */ | |
2318 int[] getColumnLengths() { | |
2319 return columnLengths; | |
2320 } | |
2321 | |
2322 /** | |
2323 * Returns the cache size used within this Response | |
2324 * | |
2325 * @return the cache size | |
2326 */ | |
2327 int getCacheSize() { | |
2328 return cacheSize; | |
2329 } | |
2330 | |
2331 /** | |
2332 * Returns the current block offset | |
2333 * | |
2334 * @return the current block offset | |
2335 */ | |
2336 int getBlockOffset() { | |
2337 return blockOffset; | |
2338 } | |
2339 | |
2340 /** | |
2341 * Returns the ResultSet type, FORWARD_ONLY or not. | |
2342 * | |
2343 * @return the ResultSet type | |
2344 */ | |
2345 int getRSType() { | |
2346 return parent.rstype; | |
2347 } | |
2348 | |
2349 /** | |
2350 * Returns the concurrency of the ResultSet. | |
2351 * | |
2352 * @return the ResultSet concurrency | |
2353 */ | |
2354 int getRSConcur() { | |
2355 return parent.rsconcur; | |
2356 } | |
2357 | |
2358 /** | |
2359 * Returns a line from the cache. If the line is already present in the | |
2360 * cache, it is returned, if not appropriate actions are taken to make | |
2361 * sure the right block is being fetched and as soon as the requested | |
2362 * line is fetched it is returned. | |
2363 * | |
2364 * @param row the row in the result set to return | |
2365 * @return the exact row read as requested or null if the requested row | |
2366 * is out of the scope of the result set | |
2367 * @throws SQLException if an database error occurs | |
2368 */ | |
2369 String getLine(final int row) throws SQLException { | |
2370 if (row >= tuplecount || row < 0) | |
2371 return null; | |
2372 | |
2373 int block = (row - blockOffset) / cacheSize; | |
2374 int blockLine = (row - blockOffset) % cacheSize; | |
2375 | |
2376 // do we have the right block loaded? (optimistic try) | |
2377 DataBlockResponse rawr = resultBlocks[block]; | |
2378 if (rawr == null) { | |
2379 // load block | |
2380 /// TODO: ponder about a maximum number of blocks to keep | |
2381 /// in memory when dealing with random access to | |
2382 /// reduce memory blow-up | |
2383 | |
2384 // if we're running forward only, we can discard the old | |
2385 // block loaded | |
2386 if (parent.rstype == ResultSet.TYPE_FORWARD_ONLY) { | |
2387 for (int i = 0; i < block; i++) | |
2388 resultBlocks[i] = null; | |
2389 | |
2390 if (MonetConnection.seqCounter - 1 == seqnr && | |
2391 !cacheSizeSetExplicitly && | |
2392 tuplecount - row > cacheSize && | |
2393 cacheSize < MonetConnection.DEF_FETCHSIZE * 10) | |
2394 { | |
2395 // there has no query been issued after this | |
2396 // one, so we can consider this an uninterrupted | |
2397 // continuation request. Let's once increase | |
2398 // the cacheSize as it was not explicitly set, | |
2399 // since the chances are high that we won't | |
2400 // bother anyone else by doing so, and just | |
2401 // gaining some performance. | |
2402 | |
2403 // store the previous position in the | |
2404 // blockOffset variable | |
2405 blockOffset += cacheSize; | |
2406 | |
2407 // increase the cache size (a lot) | |
2408 cacheSize *= 10; | |
2409 | |
2410 // by changing the cacheSize, we also | |
2411 // change the block measures. Luckily | |
2412 // we don't care about previous blocks | |
2413 // because we have a forward running | |
2414 // pointer only. However, we do have | |
2415 // to recalculate the block number, to | |
2416 // ensure the next call to find this | |
2417 // new block. | |
2418 block = (row - blockOffset) / cacheSize; | |
2419 blockLine = (row - blockOffset) % cacheSize; | |
2420 } | |
2421 } | |
2422 | |
2423 // ok, need to fetch cache block first | |
2424 parent.executeQuery(commandTempl, | |
2425 "export " + id + " " + ((block * cacheSize) + blockOffset) + " " + cacheSize); | |
2426 rawr = resultBlocks[block]; | |
2427 if (rawr == null) | |
2428 throw new SQLException("resultBlocks[" + block + "] should have been fetched by now", "M0M10"); | |
2429 } | |
2430 | |
2431 return rawr.getRow(blockLine); | |
2432 } | |
2433 | |
2434 /** | |
2435 * Closes this Response by sending an Xclose to the server indicating | |
2436 * that the result can be closed at the server side as well. | |
2437 */ | |
2438 @Override | |
2439 public void close() { | |
2440 if (closed) return; | |
2441 | |
2442 // send command to server indicating we're done with this | |
2443 // result only if we had an ID in the header and this result | |
2444 // was larger than the reply size | |
2445 try { | |
2446 if (destroyOnClose) | |
2447 sendControlCommand("close " + id); | |
2448 } catch (SQLException e) { | |
2449 // probably a connection error... | |
2450 } | |
2451 | |
2452 // close the data block associated with us | |
2453 for (int i = 1; i < resultBlocks.length; i++) { | |
2454 DataBlockResponse r = resultBlocks[i]; | |
2455 if (r != null) | |
2456 r.close(); | |
2457 } | |
2458 | |
2459 closed = true; | |
2460 } | |
2461 | |
2462 /** | |
2463 * Returns whether this Response is closed | |
2464 * | |
2465 * @return whether this Response is closed | |
2466 */ | |
2467 boolean isClosed() { | |
2468 return closed; | |
2469 } | |
2470 } | |
2471 // }}} | |
2472 | |
2473 /** | |
2474 * The DataBlockResponse is tabular data belonging to a | |
2475 * ResultSetResponse. Tabular data from the server typically looks | |
2476 * like: | |
2477 * <pre> | |
2478 * [ "value", 56 ] | |
2479 * </pre> | |
2480 * where each column is separated by ",\t" and each tuple surrounded | |
2481 * by brackets ("[" and "]"). A DataBlockResponse object holds the | |
2482 * raw data as read from the server, in a parsed manner, ready for | |
2483 * easy retrieval. | |
2484 * | |
2485 * This object is not intended to be queried by multiple threads | |
2486 * synchronously. It is designed to work for one thread retrieving | |
2487 * rows from it. When multiple threads will retrieve rows from this | |
2488 * object, it is possible for threads to get the same data. | |
2489 */ | |
2490 // {{{ DataBlockResponse class implementation | |
2491 private final static class DataBlockResponse implements Response { | |
2492 /** The String array to keep the data in */ | |
2493 private final String[] data; | |
2494 | |
2495 /** The counter which keeps the current position in the data array */ | |
2496 private int pos; | |
2497 /** Whether we can discard lines as soon as we have read them */ | |
2498 private final boolean forwardOnly; | |
2499 | |
2500 /** | |
2501 * Constructs a DataBlockResponse object | |
2502 * @param size the size of the data array to create | |
2503 * @param forward whether this is a forward only result | |
2504 */ | |
2505 DataBlockResponse(final int size, final boolean forward) { | |
2506 pos = -1; | |
2507 data = new String[size]; | |
2508 forwardOnly = forward; | |
2509 } | |
2510 | |
2511 /** | |
2512 * addLine adds a String of data to this object's data array. | |
2513 * Note that an IndexOutOfBoundsException can be thrown when an | |
2514 * attempt is made to add more than the original construction size | |
2515 * specified. | |
2516 * | |
2517 * @param line the header line as String | |
2518 * @param linetype the line type according to the MAPI protocol | |
2519 * @return a non-null String if the line is invalid, | |
2520 * or additional lines are not allowed. | |
2521 */ | |
2522 @Override | |
2523 public String addLine(final String line, final int linetype) { | |
2524 if (linetype != BufferedMCLReader.RESULT) | |
2525 return "protocol violation: unexpected line in data block: " + line; | |
2526 // add to the backing array | |
2527 data[++pos] = line; | |
2528 | |
2529 // all is well | |
2530 return null; | |
2531 } | |
2532 | |
2533 /** | |
2534 * Returns whether this Response expects more lines to be added | |
2535 * to it. | |
2536 * | |
2537 * @return true if a next line should be added, false otherwise | |
2538 */ | |
2539 @Override | |
2540 public boolean wantsMore() { | |
2541 // remember: pos is the value already stored | |
2542 return pos + 1 < data.length; | |
2543 } | |
2544 | |
2545 /** | |
2546 * Indicates that no more header lines will be added to this | |
2547 * Response implementation. In most cases this is a redundant | |
2548 * operation because the data array is full. However... it can | |
2549 * happen that this is NOT the case! | |
2550 * | |
2551 * @throws SQLException if not all rows are filled | |
2552 */ | |
2553 /* MvD: disabled not used/needed code | |
2554 @Override | |
2555 public void complete() throws SQLException { | |
2556 if ((pos + 1) != data.length) | |
2557 throw new SQLException("Inconsistent state detected! Current block capacity: " | |
2558 + data.length + ", block usage: " + (pos + 1) | |
2559 + ". Did MonetDB send what it promised to?", "M0M10"); | |
2560 } | |
2561 */ | |
2562 | |
2563 /** | |
2564 * Instructs the Response implementation to close and do the | |
2565 * necessary clean up procedures. | |
2566 */ | |
2567 @Override | |
2568 public void close() { | |
2569 // feed all rows to the garbage collector | |
2570 for (int i = 0; i < data.length; i++) | |
2571 data[i] = null; | |
2572 } | |
2573 | |
2574 /** | |
2575 * Retrieves the required row. Warning: if the requested rows | |
2576 * is out of bounds, an IndexOutOfBoundsException will be | |
2577 * thrown. | |
2578 * | |
2579 * @param line the row to retrieve | |
2580 * @return the requested row as String | |
2581 */ | |
2582 String getRow(final int line) { | |
2583 if (forwardOnly) { | |
2584 final String ret = data[line]; | |
2585 data[line] = null; | |
2586 return ret; | |
2587 } else { | |
2588 return data[line]; | |
2589 } | |
2590 } | |
2591 } | |
2592 // }}} | |
2593 | |
2594 /** | |
2595 * The UpdateResponse represents an update statement response. It | |
2596 * is issued on an UPDATE, INSERT or DELETE SQL statement. This | |
2597 * response keeps a count field that represents the affected rows | |
2598 * and a field that contains the last inserted auto-generated ID, or | |
2599 * -1 if not applicable.<br /> | |
2600 * <tt>&2 0 -1</tt> | |
2601 */ | |
2602 // {{{ UpdateResponse class implementation | |
2603 final static class UpdateResponse implements Response { | |
2604 public final long count; | |
2605 public final String lastid; | |
2606 | |
2607 public UpdateResponse(final long cnt, final String id) { | |
2608 // fill the blank finals | |
2609 this.count = cnt; | |
2610 this.lastid = id; | |
2611 } | |
2612 | |
2613 @Override | |
2614 public String addLine(final String line, final int linetype) { | |
2615 return "Header lines are not supported for an UpdateResponse"; | |
2616 } | |
2617 | |
2618 @Override | |
2619 public boolean wantsMore() { | |
2620 return false; | |
2621 } | |
2622 | |
2623 /* MvD: disabled not used/needed code | |
2624 @Override | |
2625 public void complete() { | |
2626 // empty, because there is nothing to check | |
2627 } | |
2628 */ | |
2629 | |
2630 @Override | |
2631 public void close() { | |
2632 // nothing to do here... | |
2633 } | |
2634 } | |
2635 // }}} | |
2636 | |
2637 /** | |
2638 * The SchemaResponse represents an schema modification response. | |
2639 * It is issued on statements like CREATE, DROP or ALTER TABLE. | |
2640 * This response keeps a field that represents the success state, as | |
2641 * defined by JDBC, which is currently in MonetDB's case alwats | |
2642 * SUCCESS_NO_INFO. Note that this state is not sent by the | |
2643 * server.<br /> | |
2644 * <tt>&3</tt> | |
2645 */ | |
2646 // {{{ SchemaResponse class implementation | |
2647 class SchemaResponse implements Response { | |
2648 public final int state = Statement.SUCCESS_NO_INFO; | |
2649 | |
2650 @Override | |
2651 public String addLine(final String line, final int linetype) { | |
2652 return "Header lines are not supported for a SchemaResponse"; | |
2653 } | |
2654 | |
2655 @Override | |
2656 public boolean wantsMore() { | |
2657 return false; | |
2658 } | |
2659 | |
2660 /* MvD: disabled not used/needed code | |
2661 @Override | |
2662 public void complete() { | |
2663 // empty, because there is nothing to check | |
2664 } | |
2665 */ | |
2666 | |
2667 @Override | |
2668 public void close() { | |
2669 // nothing to do here... | |
2670 } | |
2671 } | |
2672 // }}} | |
2673 | |
2674 /** | |
2675 * The AutoCommitResponse represents a transaction message. It | |
2676 * stores (a change in) the server side auto commit mode.<br /> | |
2677 * <tt>&4 (t|f)</tt> | |
2678 */ | |
2679 // {{{ AutoCommitResponse class implementation | |
2680 private final class AutoCommitResponse extends SchemaResponse { | |
2681 public final boolean autocommit; | |
2682 | |
2683 public AutoCommitResponse(final boolean ac) { | |
2684 // fill the blank final | |
2685 this.autocommit = ac; | |
2686 } | |
2687 } | |
2688 // }}} | |
2689 | |
2690 /** | |
2691 * A list of Response objects. Responses are added to this list. | |
2692 * Methods of this class are not synchronized. This is left as | |
2693 * responsibility to the caller to prevent concurrent access. | |
2694 */ | |
2695 // {{{ ResponseList class implementation | |
2696 final class ResponseList { | |
2697 /** The cache size (number of rows in a DataBlockResponse object) */ | |
2698 private final int cachesize; | |
2699 /** The maximum number of results for this query */ | |
2700 private final long maxrows; | |
2701 /** The ResultSet type to produce */ | |
2702 private final int rstype; | |
2703 /** The ResultSet concurrency to produce */ | |
2704 private final int rsconcur; | |
2705 /** The sequence number of this ResponseList */ | |
2706 private final int seqnr; | |
2707 /** A list of the Responses associated with the query, | |
2708 * in the right order */ | |
2709 private final ArrayList<Response> responses; | |
2710 /** A map of ResultSetResponses, used for additional | |
2711 * DataBlockResponse mapping */ | |
2712 private HashMap<Integer, ResultSetResponse> rsresponses; | |
2713 | |
2714 /** The current header returned by getNextResponse() */ | |
2715 private int curResponse; | |
2716 | |
2717 /** | |
2718 * Main constructor. The query argument can either be a String | |
2719 * or List. An SQLException is thrown if another object | |
2720 * instance is supplied. | |
2721 * | |
2722 * @param cachesize overall cachesize to use | |
2723 * @param maxrows maximum number of rows to allow in the set | |
2724 * @param rstype the type of result sets to produce | |
2725 * @param rsconcur the concurrency of result sets to produce | |
2726 */ | |
2727 ResponseList( | |
2728 final int cachesize, | |
2729 final long maxrows, | |
2730 final int rstype, | |
2731 final int rsconcur | |
2732 ) throws SQLException { | |
2733 this.cachesize = cachesize; | |
2734 this.maxrows = maxrows; | |
2735 this.rstype = rstype; | |
2736 this.rsconcur = rsconcur; | |
2737 responses = new ArrayList<Response>(); | |
2738 curResponse = -1; | |
2739 seqnr = MonetConnection.seqCounter++; | |
2740 } | |
2741 | |
2742 /** | |
2743 * Retrieves the next available response, or null if there are | |
2744 * no more responses. | |
2745 * | |
2746 * @return the next Response available or null | |
2747 */ | |
2748 Response getNextResponse() throws SQLException { | |
2749 if (rstype == ResultSet.TYPE_FORWARD_ONLY) { | |
2750 // free resources if we're running forward only | |
2751 if (curResponse >= 0 && curResponse < responses.size()) { | |
2752 final Response tmp = responses.get(curResponse); | |
2753 if (tmp != null) | |
2754 tmp.close(); | |
2755 responses.set(curResponse, null); | |
2756 } | |
2757 } | |
2758 curResponse++; | |
2759 if (curResponse >= responses.size()) { | |
2760 // ResponseList is obviously completed so, there are no | |
2761 // more responses | |
2762 return null; | |
2763 } else { | |
2764 // return this response | |
2765 return responses.get(curResponse); | |
2766 } | |
2767 } | |
2768 | |
2769 /** | |
2770 * Closes the Response at index i, if not null. | |
2771 * | |
2772 * @param i the index position of the header to close | |
2773 */ | |
2774 void closeResponse(final int i) { | |
2775 if (i < 0 || i >= responses.size()) | |
2776 return; | |
2777 final Response tmp = responses.set(i, null); | |
2778 if (tmp != null) | |
2779 tmp.close(); | |
2780 } | |
2781 | |
2782 /** | |
2783 * Closes the current response. | |
2784 */ | |
2785 void closeCurrentResponse() { | |
2786 closeResponse(curResponse); | |
2787 } | |
2788 | |
2789 /** | |
2790 * Closes the current and previous responses. | |
2791 */ | |
2792 void closeCurOldResponses() { | |
2793 for (int i = curResponse; i >= 0; i--) { | |
2794 closeResponse(i); | |
2795 } | |
2796 } | |
2797 | |
2798 /** | |
2799 * Closes this ResponseList by closing all the Responses in this | |
2800 * ResponseList. | |
2801 */ | |
2802 void close() { | |
2803 for (int i = 0; i < responses.size(); i++) { | |
2804 closeResponse(i); | |
2805 } | |
2806 } | |
2807 | |
2808 /** | |
2809 * Returns whether this ResponseList has still unclosed | |
2810 * Responses. | |
2811 */ | |
2812 boolean hasUnclosedResponses() { | |
2813 for (Response r : responses) { | |
2814 if (r != null) | |
2815 return true; | |
2816 } | |
2817 return false; | |
2818 } | |
2819 | |
2820 /** | |
2821 * Executes the query contained in this ResponseList, and | |
2822 * stores the Responses resulting from this query in this | |
2823 * ResponseList. | |
2824 * | |
2825 * @throws SQLException if a database error occurs | |
2826 */ | |
2827 void processQuery(final String query) throws SQLException { | |
2828 executeQuery(queryTempl, query); | |
2829 } | |
2830 | |
2831 /** | |
2832 * Internal executor of queries. | |
2833 * | |
2834 * @param templ the template to fill in | |
2835 * @param the query to execute | |
2836 * @throws SQLException if a database error occurs | |
2837 */ | |
2838 @SuppressWarnings("fallthrough") | |
2839 void executeQuery(final String[] templ, final String query) | |
2840 throws SQLException | |
2841 { | |
2842 String error = null; | |
2843 | |
2844 try { | |
2845 synchronized (server) { | |
2846 // make sure we're ready to send query; read data till we | |
2847 // have the prompt it is possible (and most likely) that we | |
2848 // already have the prompt and do not have to skip any | |
2849 // lines. Ignore errors from previous result sets. | |
2850 in.waitForPrompt(); | |
2851 | |
2852 // {{{ set reply size | |
2853 /** | |
2854 * Change the reply size of the server. If the given | |
2855 * value is the same as the current value known to use, | |
2856 * then ignore this call. If it is set to 0 we get a | |
2857 * prompt after the server sent it's header. | |
2858 */ | |
2859 int size = (cachesize == 0 ? DEF_FETCHSIZE : cachesize); | |
2860 if (maxrows > 0 && maxrows < size) | |
2861 size = (int)maxrows; | |
2862 // don't do work if it's not needed | |
2863 if (lang == LANG_SQL && size != curReplySize && templ != commandTempl) { | |
2864 sendControlCommand("reply_size " + size); | |
2865 | |
2866 // store the reply size after a successful change | |
2867 curReplySize = size; | |
2868 } | |
2869 // }}} set reply size | |
2870 | |
2871 // send query to the server | |
2872 out.writeLine( (templ[0] == null ? "" : templ[0]) + query + (templ[1] == null ? "" : templ[1]) ); | |
2873 | |
2874 // go for new results | |
2875 String tmpLine = in.readLine(); | |
2876 int linetype = in.getLineType(); | |
2877 Response res = null; | |
2878 while (linetype != BufferedMCLReader.PROMPT) { | |
2879 // each response should start with a start of header (or error) | |
2880 switch (linetype) { | |
2881 case BufferedMCLReader.SOHEADER: | |
2882 // make the response object, and fill it | |
2883 try { | |
2884 switch (sohp.parse(tmpLine)) { | |
2885 case StartOfHeaderParser.Q_PARSE: | |
2886 throw new MCLParseException("Q_PARSE header not allowed here", 1); | |
2887 case StartOfHeaderParser.Q_TABLE: | |
2888 case StartOfHeaderParser.Q_PREPARE: { | |
2889 final int id = sohp.getNextAsInt(); | |
2890 long tuplecount = sohp.getNextAsLong(); | |
2891 final int columncount = sohp.getNextAsInt(); | |
2892 final int rowcount = sohp.getNextAsInt(); | |
2893 // enforce the maxrows setting | |
2894 if (maxrows != 0 && tuplecount > maxrows) | |
2895 tuplecount = maxrows; | |
2896 res = new ResultSetResponse(id, tuplecount, columncount, rowcount, this, seqnr); | |
2897 // only add this resultset to the hashmap if it can possibly have an additional datablock | |
2898 if (rowcount < tuplecount) { | |
2899 if (rsresponses == null) | |
2900 rsresponses = new HashMap<Integer, ResultSetResponse>(); | |
2901 rsresponses.put(Integer.valueOf(id), (ResultSetResponse) res); | |
2902 } | |
2903 } break; | |
2904 case StartOfHeaderParser.Q_UPDATE: | |
2905 res = new UpdateResponse(sohp.getNextAsLong(), // count | |
2906 sohp.getNextAsString() // key-id | |
2907 ); | |
2908 break; | |
2909 case StartOfHeaderParser.Q_SCHEMA: | |
2910 res = new SchemaResponse(); | |
2911 break; | |
2912 case StartOfHeaderParser.Q_TRANS: | |
2913 final boolean ac = sohp.getNextAsString().equals("t") ? true : false; | |
2914 if (autoCommit && ac) { | |
2915 addWarning("Server enabled auto commit mode " + | |
2916 "while local state already was auto commit.", "01M11"); | |
2917 } | |
2918 autoCommit = ac; | |
2919 res = new AutoCommitResponse(ac); | |
2920 break; | |
2921 case StartOfHeaderParser.Q_BLOCK: { | |
2922 // a new block of results for a response... | |
2923 final int id = sohp.getNextAsInt(); | |
2924 sohp.getNextAsInt(); // columncount | |
2925 final int rowcount = sohp.getNextAsInt(); | |
2926 final int offset = sohp.getNextAsInt(); | |
2927 final ResultSetResponse t = (rsresponses != null) ? rsresponses.get(Integer.valueOf(id)) : null; | |
2928 if (t == null) { | |
2929 error = "M0M12!no ResultSetResponse with id " + id + " found"; | |
2930 break; | |
2931 } | |
2932 final DataBlockResponse r = new DataBlockResponse(rowcount, t.getRSType() == ResultSet.TYPE_FORWARD_ONLY); | |
2933 t.addDataBlockResponse(offset, r); | |
2934 res = r; | |
2935 } break; | |
2936 } // end of switch (sohp.parse(tmpLine)) | |
2937 } catch (MCLParseException e) { | |
2938 error = "M0M10!error while parsing start of header:\n" + | |
2939 e.getMessage() + | |
2940 " found: '" + tmpLine.charAt(e.getErrorOffset()) + "'" + | |
2941 " in: \"" + tmpLine + "\"" + | |
2942 " at pos: " + e.getErrorOffset(); | |
2943 // flush all the rest | |
2944 in.waitForPrompt(); | |
2945 linetype = in.getLineType(); | |
2946 break; | |
2947 } | |
2948 | |
2949 // immediately handle errors after parsing the header (res may be null) | |
2950 if (error != null) { | |
2951 in.waitForPrompt(); | |
2952 linetype = in.getLineType(); | |
2953 break; | |
2954 } | |
2955 | |
2956 // here we have a res object, which we can start filling | |
2957 while (res.wantsMore()) { | |
2958 error = res.addLine(in.readLine(), in.getLineType()); | |
2959 if (error != null) { | |
2960 // right, some protocol violation, | |
2961 // skip the rest of the result | |
2962 error = "M0M10!" + error; | |
2963 in.waitForPrompt(); | |
2964 linetype = in.getLineType(); | |
2965 break; | |
2966 } | |
2967 } | |
2968 if (error != null) | |
2969 break; | |
2970 | |
2971 // it is of no use to store DataBlockResponses, you never want to | |
2972 // retrieve them directly anyway | |
2973 if (!(res instanceof DataBlockResponse)) | |
2974 responses.add(res); | |
2975 | |
2976 // read the next line (can be prompt, new result, error, etc.) | |
2977 // before we start the loop over | |
2978 tmpLine = in.readLine(); | |
2979 linetype = in.getLineType(); | |
2980 break; | |
2981 case BufferedMCLReader.INFO: | |
2982 addWarning(tmpLine.substring(1), "01000"); | |
2983 // read the next line (can be prompt, new result, error, etc.) | |
2984 // before we start the loop over | |
2985 tmpLine = in.readLine(); | |
2986 linetype = in.getLineType(); | |
2987 break; | |
2988 default: // Yeah... in Java this is correct! | |
2989 // we have something we don't expect/understand, let's make it an error message | |
2990 tmpLine = "!M0M10!protocol violation, unexpected line: " + tmpLine; | |
2991 // don't break; fall through... | |
2992 case BufferedMCLReader.ERROR: | |
2993 // read everything till the prompt (should be | |
2994 // error) we don't know if we ignore some | |
2995 // garbage here... but the log should reveal that | |
2996 error = in.waitForPrompt(); | |
2997 linetype = in.getLineType(); | |
2998 if (error != null) { | |
2999 error = tmpLine.substring(1) + "\n" + error; | |
3000 } else { | |
3001 error = tmpLine.substring(1); | |
3002 } | |
3003 break; | |
3004 } // end of switch (linetype) | |
3005 } // end of while (linetype != BufferedMCLReader.PROMPT) | |
3006 } // end of synchronized (server) | |
3007 | |
3008 if (error != null) { | |
3009 SQLException ret = null; | |
3010 final String[] errors = error.split("\n"); | |
3011 for (int i = 0; i < errors.length; i++) { | |
3012 final SQLException newErr; | |
3013 if (errors[i].length() >= 6) { | |
3014 newErr = new SQLException(errors[i].substring(6), errors[i].substring(0, 5)); | |
3015 } else { | |
3016 newErr = new SQLNonTransientConnectionException(errors[i], "08000"); | |
3017 } | |
3018 if (ret == null) { | |
3019 ret = newErr; | |
3020 } else { | |
3021 ret.setNextException(newErr); | |
3022 } | |
3023 } | |
3024 throw ret; | |
3025 } | |
3026 } catch (SocketTimeoutException e) { | |
3027 close(); // JDBC 4.1 semantics, abort() | |
3028 throw new SQLNonTransientConnectionException("connection timed out", "08M33"); | |
3029 } catch (IOException e) { | |
3030 closed = true; | |
3031 throw new SQLNonTransientConnectionException(e.getMessage() + " (mserver5 still alive?)", "08006"); | |
3032 } | |
3033 } | |
3034 } | |
3035 // }}} | |
3036 } |