Source code: com/mysql/jdbc/Connection.java
1 /*
2 Copyright (C) 2002-2004 MySQL AB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of version 2 of the GNU General Public License as
6 published by the Free Software Foundation.
7
8
9 There are special exceptions to the terms and conditions of the GPL
10 as it is applied to this software. View the full text of the
11 exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
12 software distribution.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 */
24 package com.mysql.jdbc;
25
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.Reader;
29 import java.io.UnsupportedEncodingException;
30 import java.math.BigDecimal;
31 import java.net.URL;
32 import java.sql.CallableStatement;
33 import java.sql.Clob;
34 import java.sql.Date;
35 import java.sql.ParameterMetaData;
36 import java.sql.Ref;
37 import java.sql.SQLException;
38 import java.sql.Savepoint;
39 import java.sql.Time;
40 import java.sql.Timestamp;
41 import java.util.ArrayList;
42 import java.util.Calendar;
43 import java.util.HashMap;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Properties;
48 import java.util.TimeZone;
49 import java.util.TreeMap;
50
51
52 /**
53 * A Connection represents a session with a specific database. Within the
54 * context of a Connection, SQL statements are executed and results are
55 * returned.
56 *
57 * <P>
58 * A Connection's database is able to provide information describing its
59 * tables, its supported SQL grammar, its stored procedures, the capabilities
60 * of this connection, etc. This information is obtained with the getMetaData
61 * method.
62 * </p>
63 *
64 * @author Mark Matthews
65 * @version $Id: Connection.java,v 1.31.2.85 2004/11/15 00:23:07 mmatthew Exp $
66 *
67 * @see java.sql.Connection
68 */
69 public class Connection implements java.sql.Connection {
70 // The command used to "ping" the database.
71 // Newer versions of MySQL server have a ping() command,
72 // but this works for everything
73 private static final String PING_COMMAND = "SELECT 1";
74
75 /**
76 * Map mysql transaction isolation level name to
77 * java.sql.Connection.TRANSACTION_XXX
78 */
79 private static Map mapTransIsolationName2Value = null;
80
81 /**
82 * The mapping between MySQL charset names and Java charset names.
83 * Initialized by loadCharacterSetMapping()
84 */
85 private static Map charsetMap;
86
87 /** Table of multi-byte charsets. Initialized by loadCharacterSetMapping() */
88 private static Map multibyteCharsetsMap;
89
90 /** Default socket factory classname */
91 private static final String DEFAULT_SOCKET_FACTORY = StandardSocketFactory.class
92 .getName();
93
94 static {
95 loadCharacterSetMapping();
96 mapTransIsolationName2Value = new HashMap(8);
97 mapTransIsolationName2Value.put("READ-UNCOMMITED",
98 new Integer(TRANSACTION_READ_UNCOMMITTED));
99 mapTransIsolationName2Value.put("READ-UNCOMMITTED",
100 new Integer(TRANSACTION_READ_UNCOMMITTED));
101 mapTransIsolationName2Value.put("READ-COMMITTED",
102 new Integer(TRANSACTION_READ_COMMITTED));
103 mapTransIsolationName2Value.put("REPEATABLE-READ",
104 new Integer(TRANSACTION_REPEATABLE_READ));
105 mapTransIsolationName2Value.put("SERIALIZABLE",
106 new Integer(TRANSACTION_SERIALIZABLE));
107 }
108
109 /**
110 * Marker for character set converter not being available (not written,
111 * multibyte, etc) Used to prevent multiple instantiation requests.
112 */
113 private final static Object CHARSET_CONVERTER_NOT_AVAILABLE_MARKER = new Object();
114 boolean parserKnowsUnicode = false;
115
116 /** Internal DBMD to use for various database-version specific features */
117 private DatabaseMetaData dbmd = null;
118
119 /** The list of host(s) to try and connect to */
120 private List hostList = null;
121
122 /** A map of SQL to parsed prepared statement parameters. */
123 private Map cachedPreparedStatementParams;
124
125 /**
126 * Holds cached mappings to charset converters to avoid static
127 * synchronization and at the same time save memory (each charset
128 * converter takes approx 65K of static data).
129 */
130 private Map charsetConverterMap = new HashMap(CharsetMapping.JAVA_TO_MYSQL_CHARSET_MAP
131 .size());
132
133 /** A map of statements that have had setMaxRows() called on them */
134 private Map statementsUsingMaxRows;
135
136 /**
137 * The type map for UDTs (not implemented, but used by some third-party
138 * vendors, most notably IBM WebSphere)
139 */
140 private Map typeMap;
141
142 /** The I/O abstraction interface (network conn to MySQL server */
143 private MysqlIO io = null;
144
145 /** Mutex */
146 private final Object mutex = new Object();
147
148 /** The map of server variables that we retrieve at connection init. */
149 private Map serverVariables = null;
150
151 /** The driver instance that created us */
152 private NonRegisteringDriver myDriver;
153
154 /** Properties for this connection specified by user */
155 private Properties props = null;
156
157 /** The database we're currently using (called Catalog in JDBC terms). */
158 private String database = null;
159
160 /** If we're doing unicode character conversions, what encoding do we use? */
161 private String encoding = null;
162
163 /** The hostname we're connected to */
164 private String host = null;
165
166 /** The JDBC URL we're using */
167 private String myURL = null;
168
169 /** What does MySQL call this encoding? */
170 private String mysqlEncodingName = null;
171 private String negativeInfinityRep = MysqlDefs.MIN_DOUBLE_VAL_STRING;
172 private String notANumberRep = MysqlDefs.NAN_VAL_STRING;
173
174 /** The password we used */
175 private String password = null;
176 private String positiveInfinityRep = MysqlDefs.MAX_DOUBLE_VAL_STRING;
177
178 /** Classname for socket factory */
179 private String socketFactoryClassName = null;
180
181 /** The user we're connected as */
182 private String user = null;
183
184 /** Where was the connection _explicitly_ closed by the application? */
185 private Throwable explicitCloseLocation;
186
187 /** If the connection was forced closed, why was it forced closed? */
188 private Throwable forcedCloseReason;
189 private TimeZone defaultTimeZone;
190
191 /** The timezone of the server */
192 private TimeZone serverTimezone = null;
193
194 /**
195 * We need this 'bootstrapped', because 4.1 and newer will send fields back
196 * with this even before we fill this dynamically from the server.
197 */
198 private String[] indexToCharsetMapping = CharsetMapping.INDEX_TO_CHARSET;
199
200 /** Allow LOAD LOCAL INFILE (defaults to true) */
201 private boolean allowLoadLocalInfile = true;
202
203 /** Should we clear the input stream each query? */
204 private boolean alwaysClearStream = false;
205
206 /** Are we in autoCommit mode? */
207 private boolean autoCommit = true;
208
209 /** SHould we cache the parsing of prepared statements? */
210 private boolean cachePreparedStatements = false;
211
212 /** Should we capitalize mysql types */
213 private boolean capitalizeDBMDTypes = false;
214
215 /** Should we clobber streaming results on new queries, or issue an error? */
216 private boolean clobberStreamingResults = false;
217
218 /**
219 * Should we continue processing batch commands if one fails. The JDBC spec
220 * allows either way, so we let the user choose
221 */
222 private boolean continueBatchOnError = true;
223
224 /** Should we do unicode character conversions? */
225 private boolean doUnicode = false;
226
227 /** When failed-over, set connection to read-only? */
228 private boolean failOverReadOnly = true;
229
230 /** Are we failed-over to a non-master host */
231 private boolean failedOver = false;
232
233 /** Does the server suuport isolation levels? */
234 private boolean hasIsolationLevels = false;
235
236 /** Does this version of MySQL support quoted identifiers? */
237 private boolean hasQuotedIdentifiers = false;
238
239 //
240 // This is for the high availability :) routines
241 //
242 private boolean highAvailability = false;
243
244 /** Ignore non-transactional table warning for rollback? */
245 private boolean ignoreNonTxTables = false;
246
247 /** Has this connection been closed? */
248 private boolean isClosed = true;
249
250 /** Should we tell MySQL that we're an interactive client? */
251 private boolean isInteractiveClient = false;
252
253 /** Is the server configured to use lower-case table names only? */
254 private boolean lowerCaseTableNames = false;
255
256 /** Has the max-rows setting been changed from the default? */
257 private boolean maxRowsChanged = false;
258 private boolean needsPing = false;
259 private boolean negativeInfinityRepIsClipped = true;
260 private boolean notANumberRepIsClipped = true;
261
262 /** Do we expose sensitive information in exception and error messages? */
263 private boolean paranoid = false;
264
265 /** Should we do 'extra' sanity checks? */
266 private boolean pedantic = false;
267 private boolean positiveInfinityRepIsClipped = true;
268
269 /** Should we retrieve 'info' messages from the server? */
270 private boolean readInfoMsg = false;
271
272 /** Are we in read-only mode? */
273 private boolean readOnly = false;
274
275 /**
276 * If autoReconnect == true, should we attempt to reconnect at transaction
277 * boundaries?
278 */
279 private boolean reconnectAtTxEnd = false;
280
281 /** Do we relax the autoCommit semantics? (For enhydra, for example) */
282 private boolean relaxAutoCommit = false;
283
284 /** Do we need to correct endpoint rounding errors */
285 private boolean strictFloatingPoint = false;
286
287 /** Do we check all keys for updatable result sets? */
288 private boolean strictUpdates = true;
289
290 /** Are transactions supported by the MySQL server we are connected to? */
291 private boolean transactionsSupported = false;
292
293 /** Has ANSI_QUOTES been enabled on the server? */
294 private boolean useAnsiQuotes = false;
295
296 /** Should we use compression? */
297 private boolean useCompression = false;
298
299 /** Can we use the "ping" command rather than a query? */
300 private boolean useFastPing = false;
301
302 /** Should we tack on hostname in DBMD.getTable/ColumnPrivileges()? */
303 private boolean useHostsInPrivileges = true;
304
305 /** Should we only use the error message from the server when reporting
306 * errors?
307 */
308 private boolean useOnlyServerErrorMessages = true;
309
310 /** Should we use SSL? */
311 private boolean useSSL = false;
312
313 /**
314 * Should we use stream lengths in prepared statements? (true by default ==
315 * JDBC compliant)
316 */
317 private boolean useStreamLengthsInPrepStmts = true;
318
319 /** Should we use timezone information? */
320 private boolean useTimezone = false;
321
322 /** Should we return PreparedStatements for UltraDev's stupid bug? */
323 private boolean useUltraDevWorkAround = false;
324 private boolean useUnbufferedInput = true;
325 private double initialTimeout = 2.0D;
326
327 /** How many hosts are in the host list? */
328 private int hostListSize = 0;
329
330 /** isolation level */
331 private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;
332
333 /**
334 * The largest packet we can send (changed once we know what the server
335 * supports, we get this at connection init).
336 */
337 private int maxAllowedPacket = 65536;
338 private int maxReconnects = 3;
339
340 /**
341 * The max rows that a result set can contain. Defaults to -1, which
342 * according to the JDBC spec means "all".
343 */
344 private int maxRows = -1;
345 private int netBufferLength = 16384;
346
347 /** The port number we're connected to (defaults to 3306) */
348 private int port = 3306;
349
350 /**
351 * If prepared statement caching is enabled, what should the threshold
352 * length of the SQL to prepare should be in order to _not_ cache?
353 */
354 private int preparedStatementCacheMaxSqlSize = 256;
355
356 /** If prepared statement caching is enabled, how many should we cache? */
357 private int preparedStatementCacheSize = 25;
358
359 /**
360 * How many queries should we wait before we try to re-connect to the
361 * master, when we are failing over to replicated hosts Defaults to 50
362 */
363 private int queriesBeforeRetryMaster = 50;
364
365 /** What should we set the socket timeout to? */
366 private int socketTimeout = 0; // infinite
367
368 /** When did the last query finish? */
369 private long lastQueryFinishedTime = 0;
370
371 /** When did the master fail? */
372 private long masterFailTimeMillis = 0L;
373
374 /** Number of queries we've issued since the master failed */
375 private long queriesIssuedFailedOver = 0;
376
377 /**
378 * How many seconds should we wait before retrying to connect to the master
379 * if failed over? We fall back when either queriesBeforeRetryMaster or
380 * secondsBeforeRetryMaster is reached.
381 */
382 private long secondsBeforeRetryMaster = 30L;
383
384 /**
385 * Creates a connection to a MySQL Server.
386 *
387 * @param host the hostname of the database server
388 * @param port the port number the server is listening on
389 * @param info a Properties[] list holding the user and password
390 * @param database the database to connect to
391 * @param url the URL of the connection
392 * @param d the Driver instantation of the connection
393 *
394 * @exception java.sql.SQLException if a database access error occurs
395 * @throws SQLException DOCUMENT ME!
396 */
397 Connection(String host, int port, Properties info, String database,
398 String url, NonRegisteringDriver d) throws java.sql.SQLException {
399 if (Driver.TRACE) {
400 Object[] args = { host, new Integer(port), info, database, url, d };
401 Debug.methodCall(this, "constructor", args);
402 }
403
404 this.defaultTimeZone = TimeZone.getDefault();
405
406 this.serverVariables = new HashMap();
407
408 if (host == null) {
409 this.host = "localhost";
410 hostList = new ArrayList();
411 hostList.add(this.host);
412 } else if (host.indexOf(",") != -1) {
413 // multiple hosts separated by commas (failover)
414 hostList = StringUtils.split(host, ",", true);
415 } else {
416 this.host = host;
417 hostList = new ArrayList();
418 hostList.add(this.host);
419 }
420
421 hostListSize = hostList.size();
422 this.port = port;
423
424 if (database == null) {
425 database = "";
426 }
427
428 this.database = database;
429 this.myURL = url;
430 this.myDriver = d;
431 this.user = info.getProperty("user");
432 this.password = info.getProperty("password");
433
434 if ((this.user == null) || this.user.equals("")) {
435 this.user = "nobody";
436 }
437
438 if (this.password == null) {
439 this.password = "";
440 }
441
442 this.props = info;
443 initializeDriverProperties(info);
444
445 if (Driver.DEBUG) {
446 System.out.println("Connect: " + this.user + " to " + this.database);
447 }
448
449 try {
450 createNewIO(false);
451 this.dbmd = new DatabaseMetaData(this, this.database);
452 } catch (java.sql.SQLException ex) {
453 cleanup(ex);
454
455 // don't clobber SQL exceptions
456 throw ex;
457 } catch (Exception ex) {
458 cleanup(ex);
459
460 StringBuffer mesg = new StringBuffer();
461
462 if (!useParanoidErrorMessages()) {
463 mesg.append("Cannot connect to MySQL server on ");
464 mesg.append(this.host);
465 mesg.append(":");
466 mesg.append(this.port);
467 mesg.append(".\n\n");
468 mesg.append("Make sure that there is a MySQL server ");
469 mesg.append("running on the machine/port you are trying ");
470 mesg.append(
471 "to connect to and that the machine this software is "
472 + "running on ");
473 mesg.append("is able to connect to this host/port "
474 + "(i.e. not firewalled). ");
475 mesg.append(
476 "Also make sure that the server has not been started "
477 + "with the --skip-networking ");
478 mesg.append("flag.\n\n");
479 } else {
480 mesg.append("Unable to connect to database.");
481 }
482
483 mesg.append("Underlying exception: \n\n");
484 mesg.append(ex.getClass().getName());
485
486 if (!this.paranoid) {
487 mesg.append(Util.stackTraceToString(ex));
488 }
489
490 throw new java.sql.SQLException(mesg.toString(),
491 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
492 }
493 }
494
495 /**
496 * If a connection is in auto-commit mode, than all its SQL statements will
497 * be executed and committed as individual transactions. Otherwise, its
498 * SQL statements are grouped into transactions that are terminated by
499 * either commit() or rollback(). By default, new connections are in
500 * auto- commit mode. The commit occurs when the statement completes or
501 * the next execute occurs, whichever comes first. In the case of
502 * statements returning a ResultSet, the statement completes when the last
503 * row of the ResultSet has been retrieved or the ResultSet has been
504 * closed. In advanced cases, a single statement may return multiple
505 * results as well as output parameter values. Here the commit occurs
506 * when all results and output param values have been retrieved.
507 *
508 * <p>
509 * <b>Note:</b> MySQL does not support transactions, so this method is a
510 * no-op.
511 * </p>
512 *
513 * @param autoCommit - true enables auto-commit; false disables it
514 *
515 * @exception java.sql.SQLException if a database access error occurs
516 * @throws SQLException DOCUMENT ME!
517 */
518 public void setAutoCommit(boolean autoCommit) throws java.sql.SQLException {
519 if (Driver.TRACE) {
520 Object[] args = { new Boolean(autoCommit) };
521 Debug.methodCall(this, "setAutoCommit", args);
522 }
523
524 checkClosed();
525
526 if (this.transactionsSupported) {
527 // this internal value must be set first as failover depends on it
528 // being set to true to fail over (which is done by most
529 // app servers and connection pools at the end of
530 // a transaction), and the driver issues an implicit set
531 // based on this value when it (re)-connects to a server
532 // so the value holds across connections
533 //
534 this.autoCommit = autoCommit;
535
536 //
537 // This is to catch the 'edge' case of
538 // autoCommit going from true -> false
539 //
540 if ((this.highAvailability || this.failedOver) && !this.autoCommit
541 && this.needsPing) {
542 pingAndReconnect(true);
543 }
544
545 String sql = "SET autocommit=" + (autoCommit ? "1" : "0");
546 execSQL(sql, -1, this.database);
547 } else {
548 if ((autoCommit == false) && (this.relaxAutoCommit == false)) {
549 throw new SQLException("MySQL Versions Older than 3.23.15 "
550 + "do not support transactions",
551 SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
552 } else {
553 this.autoCommit = autoCommit;
554 }
555 }
556
557 return;
558 }
559
560 /**
561 * gets the current auto-commit state
562 *
563 * @return Current state of the auto-commit mode
564 *
565 * @exception java.sql.SQLException (why?)
566 *
567 * @see setAutoCommit
568 */
569 public boolean getAutoCommit() throws java.sql.SQLException {
570 if (Driver.TRACE) {
571 Object[] args = new Object[0];
572 Debug.methodCall(this, "getAutoCommit", args);
573 Debug.returnValue(this, "getAutoCommit",
574 new Boolean(this.autoCommit));
575 }
576
577 return this.autoCommit;
578 }
579
580 /**
581 * A sub-space of this Connection's database may be selected by setting a
582 * catalog name. If the driver does not support catalogs, it will
583 * silently ignore this request
584 *
585 * <p>
586 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
587 * </p>
588 *
589 * @param catalog the database for this connection to use
590 *
591 * @throws java.sql.SQLException if a database access error occurs
592 */
593 public void setCatalog(String catalog) throws java.sql.SQLException {
594 if (Driver.TRACE) {
595 Object[] args = { catalog };
596 Debug.methodCall(this, "setCatalog", args);
597 }
598
599 checkClosed();
600
601 String quotedId = this.dbmd.getIdentifierQuoteString();
602
603 if ((quotedId == null) || quotedId.equals(" ")) {
604 quotedId = "";
605 }
606
607 StringBuffer query = new StringBuffer("USE ");
608 query.append(quotedId);
609 query.append(catalog);
610 query.append(quotedId);
611
612 execSQL(query.toString(), -1, catalog);
613 this.database = catalog;
614 }
615
616 /**
617 * Return the connections current catalog name, or null if no catalog name
618 * is set, or we dont support catalogs.
619 *
620 * <p>
621 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
622 * </p>
623 *
624 * @return the current catalog name or null
625 *
626 * @exception java.sql.SQLException if a database access error occurs
627 */
628 public String getCatalog() throws java.sql.SQLException {
629 if (Driver.TRACE) {
630 Object[] args = new Object[0];
631 Debug.methodCall(this, "getCatalog", args);
632 Debug.returnValue(this, "getCatalog", this.database);
633 }
634
635 return this.database;
636 }
637
638 /**
639 * Returns whether we clobber streaming results on new queries, or issue an
640 * error?
641 *
642 * @return true if we should implicitly close streaming result sets upon
643 * receiving a new query
644 */
645 public boolean getClobberStreamingResults() {
646 return this.clobberStreamingResults;
647 }
648
649 /**
650 * DOCUMENT ME!
651 *
652 * @return DOCUMENT ME!
653 */
654 public boolean isClosed() {
655 if (Driver.TRACE) {
656 Object[] args = new Object[0];
657 Debug.methodCall(this, "isClosed", args);
658 Debug.returnValue(this, "isClosed", new Boolean(this.isClosed));
659 }
660
661 return this.isClosed;
662 }
663
664 /**
665 * Returns the character encoding for this Connection
666 *
667 * @return the character encoding for this connection.
668 */
669 public String getEncoding() {
670 return this.encoding;
671 }
672
673 /**
674 * @see Connection#setHoldability(int)
675 */
676 public void setHoldability(int arg0) throws SQLException {
677 // do nothing
678 }
679
680 /**
681 * @see Connection#getHoldability()
682 */
683 public int getHoldability() throws SQLException {
684 return ResultSet.CLOSE_CURSORS_AT_COMMIT;
685 }
686
687 /**
688 * NOT JDBC-Compliant, but clients can use this method to determine how
689 * long this connection has been idle. This time (reported in
690 * milliseconds) is updated once a query has completed.
691 *
692 * @return number of ms that this connection has been idle, 0 if the driver
693 * is busy retrieving results.
694 */
695 public long getIdleFor() {
696 if (this.lastQueryFinishedTime == 0) {
697 return 0;
698 } else {
699 long now = System.currentTimeMillis();
700 long idleTime = now - this.lastQueryFinishedTime;
701
702 return idleTime;
703 }
704 }
705
706 /**
707 * Should we tell MySQL that we're an interactive client
708 *
709 * @return true if isInteractiveClient was set to true.
710 */
711 public boolean isInteractiveClient() {
712 return isInteractiveClient;
713 }
714
715 /**
716 * A connection's database is able to provide information describing its
717 * tables, its supported SQL grammar, its stored procedures, the
718 * capabilities of this connection, etc. This information is made
719 * available through a DatabaseMetaData object.
720 *
721 * @return a DatabaseMetaData object for this connection
722 *
723 * @exception java.sql.SQLException if a database access error occurs
724 */
725 public java.sql.DatabaseMetaData getMetaData() throws java.sql.SQLException {
726 checkClosed();
727
728 return new DatabaseMetaData(this, this.database);
729 }
730
731 /**
732 * DOCUMENT ME!
733 *
734 * @return
735 */
736 public String getNegativeInfinityRep() {
737 return negativeInfinityRep;
738 }
739
740 /**
741 * DOCUMENT ME!
742 *
743 * @return
744 */
745 public boolean isNegativeInfinityRepIsClipped() {
746 return negativeInfinityRepIsClipped;
747 }
748
749 /**
750 * DOCUMENT ME!
751 *
752 * @return
753 */
754 public String getNotANumberRep() {
755 return notANumberRep;
756 }
757
758 /**
759 * DOCUMENT ME!
760 *
761 * @return
762 */
763 public boolean isNotANumberRepIsClipped() {
764 return notANumberRepIsClipped;
765 }
766
767 /**
768 * DOCUMENT ME!
769 *
770 * @return
771 */
772 public String getPositiveInfinityRep() {
773 return positiveInfinityRep;
774 }
775
776 /**
777 * DOCUMENT ME!
778 *
779 * @return
780 */
781 public boolean isPositiveInfinityRepIsClipped() {
782 return positiveInfinityRepIsClipped;
783 }
784
785 /**
786 * Should the driver do profiling?
787 *
788 * @param flag set to true to enable profiling.
789 *
790 * @throws SQLException if the connection is closed.
791 */
792 public void setProfileSql(boolean flag) throws SQLException {
793 // For re-connection
794 this.props.setProperty("profileSql", String.valueOf(flag));
795 getIO().setProfileSql(flag);
796 }
797
798 /**
799 * You can put a connection in read-only mode as a hint to enable database
800 * optimizations <B>Note:</B> setReadOnly cannot be called while in the
801 * middle of a transaction
802 *
803 * @param readOnly - true enables read-only mode; false disables it
804 *
805 * @exception java.sql.SQLException if a database access error occurs
806 */
807 public void setReadOnly(boolean readOnly) throws java.sql.SQLException {
808 if (Driver.TRACE) {
809 Object[] args = { new Boolean(readOnly) };
810 Debug.methodCall(this, "setReadOnly", args);
811 Debug.returnValue(this, "setReadOnly", new Boolean(readOnly));
812 }
813
814 checkClosed();
815 this.readOnly = readOnly;
816 }
817
818 /**
819 * Tests to see if the connection is in Read Only Mode. Note that we
820 * cannot really put the database in read only mode, but we pretend we can
821 * by returning the value of the readOnly flag
822 *
823 * @return true if the connection is read only
824 *
825 * @exception java.sql.SQLException if a database access error occurs
826 */
827 public boolean isReadOnly() throws java.sql.SQLException {
828 if (Driver.TRACE) {
829 Object[] args = new Object[0];
830 Debug.methodCall(this, "isReadOnly", args);
831 Debug.returnValue(this, "isReadOnly", new Boolean(this.readOnly));
832 }
833
834 return this.readOnly;
835 }
836
837 /**
838 * @see Connection#setSavepoint()
839 */
840 public java.sql.Savepoint setSavepoint() throws SQLException {
841 throw new NotImplemented();
842 }
843
844 /**
845 * @see Connection#setSavepoint(String)
846 */
847 public java.sql.Savepoint setSavepoint(String arg0)
848 throws SQLException {
849 throw new NotImplemented();
850 }
851
852 /**
853 * DOCUMENT ME!
854 *
855 * @return DOCUMENT ME!
856 */
857 public TimeZone getServerTimezone() {
858 return this.serverTimezone;
859 }
860
861 /**
862 * DOCUMENT ME!
863 *
864 * @param level DOCUMENT ME!
865 *
866 * @throws java.sql.SQLException DOCUMENT ME!
867 * @throws SQLException DOCUMENT ME!
868 */
869 public void setTransactionIsolation(int level) throws java.sql.SQLException {
870 if (Driver.TRACE) {
871 Object[] args = { new Integer(level) };
872 Debug.methodCall(this, "setTransactionIsolation", args);
873 }
874
875 checkClosed();
876
877 if (this.hasIsolationLevels) {
878 StringBuffer sql = new StringBuffer(
879 "SET SESSION TRANSACTION ISOLATION LEVEL ");
880
881 switch (level) {
882 case java.sql.Connection.TRANSACTION_NONE:
883 throw new SQLException("Transaction isolation level "
884 + "NONE not supported by MySQL");
885
886 case java.sql.Connection.TRANSACTION_READ_COMMITTED:
887 sql.append("READ COMMITTED");
888
889 break;
890
891 case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
892 sql.append("READ UNCOMMITTED");
893
894 break;
895
896 case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
897 sql.append("REPEATABLE READ");
898
899 break;
900
901 case java.sql.Connection.TRANSACTION_SERIALIZABLE:
902 sql.append("SERIALIZABLE");
903
904 break;
905
906 default:
907 throw new SQLException("Unsupported transaction "
908 + "isolation level '" + level + "'", "S1C00");
909 }
910
911 execSQL(sql.toString(), -1, this.database);
912 isolationLevel = level;
913 } else {
914 throw new java.sql.SQLException("Transaction Isolation Levels are "
915 + "not supported on MySQL versions older than 3.23.36.", "S1C00");
916 }
917 }
918
919 /**
920 * Get this Connection's current transaction isolation mode.
921 *
922 * @return the current TRANSACTION_ mode value
923 *
924 * @exception java.sql.SQLException if a database access error occurs
925 * @throws SQLException DOCUMENT ME!
926 */
927 public int getTransactionIsolation() throws java.sql.SQLException {
928 if (Driver.TRACE) {
929 Object[] args = new Object[0];
930 Debug.methodCall(this, "getTransactionIsolation", args);
931 Debug.returnValue(this, "getTransactionIsolation",
932 new Integer(isolationLevel));
933 }
934
935 if (this.hasIsolationLevels) {
936 java.sql.Statement stmt = null;
937 java.sql.ResultSet rs = null;
938
939 try {
940 stmt = this.createStatement();
941
942 if (stmt.getMaxRows() != 0) {
943 stmt.setMaxRows(0);
944 }
945
946 String query = null;
947
948 if (this.io.versionMeetsMinimum(4, 0, 3)) {
949 query = "SHOW VARIABLES LIKE 'tx_isolation'";
950 } else {
951 query = "SHOW VARIABLES LIKE 'transaction_isolation'";
952 }
953
954 rs = stmt.executeQuery(query);
955
956 if (rs.next()) {
957 String s = rs.getString(2);
958
959 if (s != null) {
960 Integer intTI = (Integer) mapTransIsolationName2Value
961 .get(s);
962
963 if (intTI != null) {
964 return intTI.intValue();
965 }
966 }
967
968 throw new SQLException(
969 "Could not map transaction isolation '" + s
970 + " to a valid JDBC level.",
971 SQLError.SQL_STATE_GENERAL_ERROR);
972 } else {
973 throw new SQLException("Could not retrieve transaction isolation level from server",
974 SQLError.SQL_STATE_GENERAL_ERROR);
975 }
976 } finally {
977 if (rs != null) {
978 try {
979 rs.close();
980 } catch (Exception ex) {
981 // ignore
982 }
983
984 rs = null;
985 }
986
987 if (stmt != null) {
988 try {
989 stmt.close();
990 } catch (Exception ex) {
991 // ignore
992 }
993
994 stmt = null;
995 }
996 }
997 }
998
999 return isolationLevel;
1000 }
1001
1002 /**
1003 * JDBC 2.0 Install a type-map object as the default type-map for this
1004 * connection
1005 *
1006 * @param map the type mapping
1007 *
1008 * @throws SQLException if a database error occurs.
1009 */
1010 public void setTypeMap(java.util.Map map) throws SQLException {
1011 this.typeMap = map;
1012 }
1013
1014 /**
1015 * JDBC 2.0 Get the type-map object associated with this connection. By
1016 * default, the map returned is empty.
1017 *
1018 * @return the type map
1019 *
1020 * @throws SQLException if a database error occurs
1021 */
1022 public synchronized java.util.Map getTypeMap() throws SQLException {
1023 if (this.typeMap == null) {
1024 this.typeMap = new HashMap();
1025 }
1026
1027 return this.typeMap;
1028 }
1029
1030 /**
1031 * The first warning reported by calls on this Connection is returned.
1032 * <B>Note:</B> Sebsequent warnings will be changed to this
1033 * java.sql.SQLWarning
1034 *
1035 * @return the first java.sql.SQLWarning or null
1036 *
1037 * @exception java.sql.SQLException if a database access error occurs
1038 */
1039 public java.sql.SQLWarning getWarnings() throws java.sql.SQLException {
1040 if (Driver.TRACE) {
1041 Object[] args = new Object[0];
1042 Debug.methodCall(this, "getWarnings", args);
1043 Debug.returnValue(this, "getWarnings", null);
1044 }
1045
1046 return null;
1047 }
1048
1049 /**
1050 * Allow use of LOAD LOCAL INFILE?
1051 *
1052 * @return true if allowLoadLocalInfile was set to true.
1053 */
1054 public boolean allowLoadLocalInfile() {
1055 return this.allowLoadLocalInfile;
1056 }
1057
1058 /**
1059 * DOCUMENT ME!
1060 *
1061 * @return DOCUMENT ME!
1062 */
1063 public boolean capitalizeDBMDTypes() {
1064 return this.capitalizeDBMDTypes;
1065 }
1066
1067 /**
1068 * Changes the user on this connection by performing a re-authentication.
1069 * If authentication fails, the connection will remain under the context
1070 * of the current user.
1071 *
1072 * @param userName the username to authenticate with
1073 * @param newPassword the password to authenticate with
1074 *
1075 * @throws SQLException if authentication fails, or some other error occurs
1076 * while performing the command.
1077 */
1078 public void changeUser(String userName, String newPassword)
1079 throws SQLException {
1080 if ((userName == null) || userName.equals("")) {
1081 userName = "";
1082 }
1083
1084 if (newPassword == null) {
1085 newPassword = "";
1086 }
1087
1088 this.io.changeUser(userName, newPassword, this.database);
1089 this.user = userName;
1090 this.password = newPassword;
1091
1092 if (this.io.versionMeetsMinimum(4, 1, 0)) {
1093 configureClientCharacterSet();
1094 }
1095 }
1096
1097 /**
1098 * After this call, getWarnings returns null until a new warning is
1099 * reported for this connection.
1100 *
1101 * @exception java.sql.SQLException if a database access error occurs
1102 */
1103 public void clearWarnings() throws java.sql.SQLException {
1104 if (Driver.TRACE) {
1105 Object[] args = new Object[0];
1106 Debug.methodCall(this, "clearWarnings", args);
1107 }
1108
1109 // firstWarning = null;
1110 }
1111
1112 /**
1113 * In some cases, it is desirable to immediately release a Connection's
1114 * database and JDBC resources instead of waiting for them to be
1115 * automatically released (cant think why off the top of my head)
1116 * <B>Note:</B> A Connection is automatically closed when it is garbage
1117 * collected. Certain fatal errors also result in a closed connection.
1118 *
1119 * @exception java.sql.SQLException if a database access error occurs
1120 */
1121 public void close() throws java.sql.SQLException {
1122 if (this.explicitCloseLocation == null) {
1123 this.explicitCloseLocation = new Throwable();
1124 }
1125
1126 realClose(true, true);
1127 }
1128
1129 /**
1130 * The method commit() makes all changes made since the previous
1131 * commit/rollback permanent and releases any database locks currently
1132 * held by the Connection. This method should only be used when
1133 * auto-commit has been disabled.
1134 *
1135 * @exception java.sql.SQLException if a database access error occurs
1136 *
1137 * @see setAutoCommit
1138 */
1139 public void commit() throws java.sql.SQLException {
1140 if (Driver.TRACE) {
1141 Object[] args = new Object[0];
1142 Debug.methodCall(this, "commit", args);
1143 }
1144
1145 checkClosed();
1146
1147 try {
1148 // no-op if _relaxAutoCommit == true
1149 if (this.autoCommit && !this.relaxAutoCommit) {
1150 throw new SQLException("Can't call commit when autocommit=true",
1151 SQLError.SQL_STATE_GENERAL_ERROR);
1152 } else if (this.transactionsSupported) {
1153 execSQL("commit", -1, this.database);
1154 }
1155 } finally {
1156 if (this.reconnectAtTxEnd) {
1157 pingAndReconnect(true);
1158 }
1159 }
1160
1161 return;
1162 }
1163
1164 //--------------------------JDBC 2.0-----------------------------
1165
1166 /**
1167 * JDBC 2.0 Same as createStatement() above, but allows the default result
1168 * set type and result set concurrency type to be overridden.
1169 *
1170 * @param resultSetType a result set type, see ResultSet.TYPE_XXX
1171 * @param resultSetConcurrency a concurrency type, see ResultSet.CONCUR_XXX
1172 *
1173 * @return a new Statement object
1174 *
1175 * @exception SQLException if a database-access error occurs.
1176 */
1177 public java.sql.Statement createStatement(int resultSetType,
1178 int resultSetConcurrency) throws SQLException {
1179 checkClosed();
1180
1181 Statement stmt = new com.mysql.jdbc.Statement(this, this.database);
1182 stmt.setResultSetType(resultSetType);
1183 stmt.setResultSetConcurrency(resultSetConcurrency);
1184
1185 return stmt;
1186 }
1187
1188 /**
1189 * SQL statements without parameters are normally executed using Statement
1190 * objects. If the same SQL statement is executed many times, it is more
1191 * efficient to use a PreparedStatement
1192 *
1193 * @return a new Statement object
1194 *
1195 * @throws SQLException passed through from the constructor
1196 */
1197 public java.sql.Statement createStatement() throws SQLException {
1198 return createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
1199 java.sql.ResultSet.CONCUR_READ_ONLY);
1200 }
1201
1202 /**
1203 * @see Connection#createStatement(int, int, int)
1204 */
1205 public java.sql.Statement createStatement(int resultSetType,
1206 int resultSetConcurrency, int resultSetHoldability)
1207 throws SQLException {
1208 if (this.pedantic) {
1209 if (resultSetHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
1210 throw new SQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
1211 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1212 }
1213 }
1214
1215 return createStatement(resultSetType, resultSetConcurrency);
1216 }
1217
1218 /**
1219 * Cleanup the connection.
1220 *
1221 * @throws Throwable if an error occurs during cleanup.
1222 */
1223 protected void finalize() throws Throwable {
1224 cleanup(null);
1225 }
1226
1227 /**
1228 * Is the server configured to use lower-case table names only?
1229 *
1230 * @return true if lower_case_table_names is 'on'
1231 */
1232 public boolean lowerCaseTableNames() {
1233 return this.lowerCaseTableNames;
1234 }
1235
1236 /**
1237 * A driver may convert the JDBC sql grammar into its system's native SQL
1238 * grammar prior to sending it; nativeSQL returns the native form of the
1239 * statement that the driver would have sent.
1240 *
1241 * @param sql a SQL statement that may contain one or more '?' parameter
1242 * placeholders
1243 *
1244 * @return the native form of this statement
1245 *
1246 * @exception java.sql.SQLException if a database access error occurs
1247 */
1248 public String nativeSQL(String sql) throws java.sql.SQLException {
1249 if (Driver.TRACE) {
1250 Object[] args = { sql };
1251 Debug.methodCall(this, "nativeSQL", args);
1252 Debug.returnValue(this, "nativeSQL", sql);
1253 }
1254
1255 return EscapeProcessor.escapeSQL(sql,
1256 getIO().versionMeetsMinimum(4, 0, 2));
1257 }
1258
1259 /**
1260 * DOCUMENT ME!
1261 *
1262 * @return DOCUMENT ME!
1263 */
1264 public boolean parserKnowsUnicode() {
1265 return this.parserKnowsUnicode;
1266 }
1267
1268 /**
1269 * DOCUMENT ME!
1270 *
1271 * @param sql DOCUMENT ME!
1272 *
1273 * @return DOCUMENT ME!
1274 *
1275 * @throws java.sql.SQLException DOCUMENT ME!
1276 */
1277 public java.sql.CallableStatement prepareCall(String sql)
1278 throws java.sql.SQLException {
1279 if (this.getUseUltraDevWorkAround()) {
1280 return new UltraDevWorkAround(prepareStatement(sql));
1281 } else {
1282 throw new java.sql.SQLException("Callable statments not "
1283 + "supported.", "S1C00");
1284 }
1285 }
1286
1287 /**
1288 * JDBC 2.0 Same as prepareCall() above, but allows the default result set
1289 * type and result set concurrency type to be overridden.
1290 *
1291 * @param sql the SQL representing the callable statement
1292 * @param resultSetType a result set type, see ResultSet.TYPE_XXX
1293 * @param resultSetConcurrency a concurrency type, see ResultSet.CONCUR_XXX
1294 *
1295 * @return a new CallableStatement object containing the pre-compiled SQL
1296 * statement
1297 *
1298 * @exception SQLException if a database-access error occurs.
1299 */
1300 public java.sql.CallableStatement prepareCall(String sql,
1301 int resultSetType, int resultSetConcurrency) throws SQLException {
1302 return prepareCall(sql);
1303 }
1304
1305 /**
1306 * @see Connection#prepareCall(String, int, int, int)
1307 */
1308 public java.sql.CallableStatement prepareCall(String sql,
1309 int resultSetType, int resultSetConcurrency, int resultSetHoldability)
1310 throws SQLException {
1311 if (this.pedantic) {
1312 if (resultSetHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
1313 throw new SQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
1314 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1315 }
1316 }
1317
1318 throw new NotImplemented();
1319 }
1320
1321 /**
1322 * A SQL statement with or without IN parameters can be pre-compiled and
1323 * stored in a PreparedStatement object. This object can then be used to
1324 * efficiently execute this statement multiple times.
1325 *
1326 * <p>
1327 * <B>Note:</B> This method is optimized for handling parametric SQL
1328 * statements that benefit from precompilation if the driver supports
1329 * precompilation. In this case, the statement is not sent to the database
1330 * until the PreparedStatement is executed. This has no direct effect on
1331 * users; however it does affect which method throws certain
1332 * java.sql.SQLExceptions
1333 * </p>
1334 *
1335 * <p>
1336 * MySQL does not support precompilation of statements, so they are handled
1337 * by the driver.
1338 * </p>
1339 *
1340 * @param sql a SQL statement that may contain one or more '?' IN parameter
1341 * placeholders
1342 *
1343 * @return a new PreparedStatement object containing the pre-compiled
1344 * statement.
1345 *
1346 * @exception java.sql.SQLException if a database access error occurs.
1347 */
1348 public java.sql.PreparedStatement prepareStatement(String sql)
1349 throws java.sql.SQLException {
1350 return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
1351 java.sql.ResultSet.CONCUR_READ_ONLY);
1352 }
1353
1354 /**
1355 * JDBC 2.0 Same as prepareStatement() above, but allows the default result
1356 * set type and result set concurrency type to be overridden.
1357 *
1358 * @param sql the SQL query containing place holders
1359 * @param resultSetType a result set type, see ResultSet.TYPE_XXX
1360 * @param resultSetConcurrency a concurrency type, see ResultSet.CONCUR_XXX
1361 *
1362 * @return a new PreparedStatement object containing the pre-compiled SQL
1363 * statement
1364 *
1365 * @exception SQLException if a database-access error occurs.
1366 */
1367 public synchronized java.sql.PreparedStatement prepareStatement(
1368 String sql, int resultSetType, int resultSetConcurrency)
1369 throws SQLException {
1370 checkClosed();
1371
1372 PreparedStatement pStmt = null;
1373
1374 if (this.cachePreparedStatements) {
1375 PreparedStat