EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.jdbc]

COVERAGE SUMMARY FOR SOURCE FILE [JdbcConnection.java]

nameclass, %method, %block, %line, %
JdbcConnection.java100% (1/1)94%  (88/94)87%  (2446/2812)83%  (588.1/705)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JdbcConnection100% (1/1)94%  (88/94)87%  (2446/2812)83%  (588.1/705)
abort (Executor): void 0%   (0/1)0%   (0/1)0%   (0/1)
getNetworkTimeout (): int 0%   (0/1)0%   (0/2)0%   (0/1)
getSchema (): String 0%   (0/1)0%   (0/2)0%   (0/1)
isValid (int): boolean 0%   (0/1)0%   (0/26)0%   (0/8)
setNetworkTimeout (Executor, int): void 0%   (0/1)0%   (0/1)0%   (0/1)
setSchema (String): void 0%   (0/1)0%   (0/1)0%   (0/1)
convertToClientInfoException (SQLException): SQLClientInfoException 100% (1/1)33%  (6/18)67%  (2/3)
convertSavepoint (Savepoint): JdbcSavepoint 100% (1/1)35%  (6/17)67%  (2/3)
checkTypeConcurrency (int, int): void 100% (1/1)41%  (7/17)71%  (5/7)
checkClosed (boolean): void 100% (1/1)53%  (19/36)60%  (6/10)
rollback (): void 100% (1/1)55%  (12/22)74%  (6.7/9)
checkHoldability (int): void 100% (1/1)58%  (7/12)67%  (2/3)
clearWarnings (): void 100% (1/1)58%  (7/12)67%  (4/6)
getTypeMap (): Map 100% (1/1)58%  (7/12)60%  (3/5)
getWarnings (): SQLWarning 100% (1/1)58%  (7/12)60%  (3/5)
getHoldability (): int 100% (1/1)62%  (8/13)60%  (3/5)
setCatalog (String): void 100% (1/1)62%  (8/13)67%  (4/6)
getAutoCommit (): boolean 100% (1/1)64%  (9/14)60%  (3/5)
releaseSavepoint (Savepoint): void 100% (1/1)67%  (10/15)71%  (5/7)
getTransactionIsolation (): int 100% (1/1)68%  (39/57)72%  (13/18)
isClosed (): boolean 100% (1/1)74%  (14/19)50%  (2/4)
setHoldability (int): void 100% (1/1)74%  (14/19)75%  (6/8)
setTransactionIsolation (int): void 100% (1/1)75%  (40/53)72%  (13/18)
close (): void 100% (1/1)79%  (70/89)72%  (21/29)
createBlob (): Blob 100% (1/1)79%  (37/47)76%  (6.9/9)
setReadOnly (boolean): void 100% (1/1)79%  (19/24)71%  (5/7)
createClob (): Clob 100% (1/1)80%  (40/50)76%  (6.9/9)
createNClob (): NClob 100% (1/1)80%  (40/50)76%  (6.9/9)
getQueryTimeout (): int 100% (1/1)81%  (48/59)79%  (11/14)
getMetaData (): DatabaseMetaData 100% (1/1)81%  (22/27)71%  (5/7)
createStatement (): Statement 100% (1/1)82%  (23/28)71%  (5/7)
prepareStatement (String, int): PreparedStatement 100% (1/1)83%  (24/29)60%  (3/5)
prepareStatement (String, String []): PreparedStatement 100% (1/1)83%  (25/30)60%  (3/5)
prepareStatement (String, int []): PreparedStatement 100% (1/1)83%  (25/30)60%  (3/5)
getPowerOffCount (): int 100% (1/1)85%  (11/13)84%  (0.8/1)
isReadOnly (): boolean 100% (1/1)86%  (30/35)78%  (7/9)
setAutoCommit (boolean): void 100% (1/1)86%  (31/36)80%  (8/10)
getCatalog (): String 100% (1/1)87%  (33/38)82%  (9/11)
setQueryTimeout (int): void 100% (1/1)88%  (35/40)80%  (8/10)
prepareCall (String): CallableStatement 100% (1/1)88%  (36/41)75%  (6/8)
prepareAutoCloseStatement (String): PreparedStatement 100% (1/1)88%  (37/42)75%  (6/8)
createStatement (int, int): Statement 100% (1/1)89%  (39/44)75%  (6/8)
createStatement (int, int, int): Statement 100% (1/1)90%  (45/50)78%  (7/9)
prepareCall (String, int, int): CallableStatement 100% (1/1)90%  (47/52)78%  (7/9)
prepareStatement (String, int, int): PreparedStatement 100% (1/1)91%  (48/53)78%  (7/9)
getClientInfo (): Properties 100% (1/1)91%  (49/54)82%  (9/11)
setSavepoint (): Savepoint 100% (1/1)91%  (51/56)82%  (9/11)
prepareCall (String, int, int, int): CallableStatement 100% (1/1)91%  (53/58)80%  (8/10)
setSavepoint (String): Savepoint 100% (1/1)91%  (53/58)80%  (8/10)
prepareStatement (String, int, int, int): PreparedStatement 100% (1/1)92%  (54/59)80%  (8/10)
translateGetEnd (String, int, char): int 100% (1/1)94%  (162/172)97%  (33/34)
translateSQL (String, boolean): String 100% (1/1)95%  (254/266)96%  (77/80)
JdbcConnection (ConnectionInfo, boolean): void 100% (1/1)97%  (94/97)95%  (21/22)
JdbcConnection (JdbcConnection): void 100% (1/1)100% (71/71)100% (19/19)
JdbcConnection (SessionInterface, String, String): void 100% (1/1)100% (39/39)100% (12/12)
JdbcConnection (String, Properties): void 100% (1/1)100% (9/9)100% (2/2)
afterWriting (): void 100% (1/1)100% (7/7)100% (3/3)
checkClosed (): void 100% (1/1)100% (4/4)100% (2/2)
checkClosedForWrite (): void 100% (1/1)100% (4/4)100% (2/2)
checkMap (Map): void 100% (1/1)100% (9/9)100% (3/3)
checkRunOver (int, int, String): void 100% (1/1)100% (8/8)100% (3/3)
closeAndSetNull (CommandInterface): CommandInterface 100% (1/1)100% (6/6)100% (3/3)
closeOld (): void 100% (1/1)100% (30/30)100% (13/13)
closePreparedCommands (): void 100% (1/1)100% (41/41)100% (9/9)
commit (): void 100% (1/1)100% (31/31)100% (10/10)
convertToDefaultObject (Value): Object 100% (1/1)100% (40/40)100% (12/12)
createArrayOf (String, Object []): Array 100% (1/1)100% (4/4)100% (1/1)
createBlob (InputStream, long): Value 100% (1/1)100% (24/24)100% (7/7)
createClob (Reader, long): Value 100% (1/1)100% (24/24)100% (7/7)
createSQLXML (): SQLXML 100% (1/1)100% (4/4)100% (1/1)
createStruct (String, Object []): Struct 100% (1/1)100% (4/4)100% (1/1)
found (String, int, String): boolean 100% (1/1)100% (9/9)100% (1/1)
getClientInfo (String): String 100% (1/1)100% (29/29)100% (10/10)
getCompareMode (): CompareMode 100% (1/1)100% (3/3)100% (1/1)
getGeneratedKeys (JdbcStatement, int): ResultSet 100% (1/1)100% (26/26)100% (4/4)
getSession (): SessionInterface 100% (1/1)100% (3/3)100% (1/1)
getURL (): String 100% (1/1)100% (5/5)100% (2/2)
getUser (): String 100% (1/1)100% (5/5)100% (2/2)
isWrapperFor (Class): boolean 100% (1/1)100% (11/11)100% (1/1)
nativeSQL (String): String 100% (1/1)100% (14/14)100% (5/5)
prepareCommand (String, CommandInterface): CommandInterface 100% (1/1)100% (10/10)100% (1/1)
prepareCommand (String, int): CommandInterface 100% (1/1)100% (6/6)100% (1/1)
prepareStatement (String): PreparedStatement 100% (1/1)100% (42/42)100% (8/8)
rollback (Savepoint): void 100% (1/1)100% (35/35)100% (10/10)
rollbackInternal (): void 100% (1/1)100% (12/12)100% (3/3)
setClientInfo (Properties): void 100% (1/1)100% (18/18)100% (6/6)
setClientInfo (String, String): void 100% (1/1)100% (33/33)100% (6/6)
setExecutingStatement (Statement): void 100% (1/1)100% (4/4)100% (2/2)
setPowerOffCount (int): void 100% (1/1)100% (8/8)100% (3/3)
setTraceLevel (int): void 100% (1/1)100% (5/5)100% (2/2)
setTypeMap (Map): void 100% (1/1)100% (22/22)100% (6/6)
toString (): String 100% (1/1)100% (18/18)100% (1/1)
translateSQL (String): String 100% (1/1)100% (4/4)100% (1/1)
unwrap (Class): Object 100% (1/1)100% (10/10)100% (3/3)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.jdbc;
7 
8import java.io.ByteArrayInputStream;
9import java.io.InputStream;
10import java.io.InputStreamReader;
11import java.io.Reader;
12import java.sql.Array;
13import java.sql.Blob;
14import java.sql.CallableStatement;
15import java.sql.Clob;
16import java.sql.Connection;
17import java.sql.DatabaseMetaData;
18import java.sql.NClob;
19import java.sql.PreparedStatement;
20import java.sql.ResultSet;
21import java.sql.SQLClientInfoException;
22import java.sql.SQLException;
23import java.sql.SQLWarning;
24import java.sql.SQLXML;
25import java.sql.Savepoint;
26import java.sql.Statement;
27import java.sql.Struct;
28import java.util.ArrayList;
29import java.util.Map;
30import java.util.Properties;
31 
32import org.h2.api.ErrorCode;
33import org.h2.command.CommandInterface;
34import org.h2.engine.ConnectionInfo;
35import org.h2.engine.Constants;
36import org.h2.engine.SessionInterface;
37import org.h2.engine.SessionRemote;
38import org.h2.engine.SysProperties;
39import org.h2.message.DbException;
40import org.h2.message.TraceObject;
41import org.h2.result.ResultInterface;
42import org.h2.util.CloseWatcher;
43import org.h2.util.JdbcUtils;
44import org.h2.util.Utils;
45import org.h2.value.CompareMode;
46import org.h2.value.Value;
47import org.h2.value.ValueInt;
48import org.h2.value.ValueNull;
49import org.h2.value.ValueString;
50 
51//## Java 1.7 ##
52import java.util.concurrent.Executor;
53//*/
54 
55/**
56 * <p>
57 * Represents a connection (session) to a database.
58 * </p>
59 * <p>
60 * Thread safety: the connection is thread-safe, because access
61 * is synchronized. However, for compatibility with other databases, a
62 * connection should only be used in one thread at any time.
63 * </p>
64 */
65public class JdbcConnection extends TraceObject implements Connection {
66 
67    private static boolean keepOpenStackTrace;
68 
69    private final String url;
70    private final String user;
71 
72    // ResultSet.HOLD_CURSORS_OVER_COMMIT
73    private int holdability = 1;
74 
75    private SessionInterface session;
76    private CommandInterface commit, rollback;
77    private CommandInterface getReadOnly, getGeneratedKeys;
78    private CommandInterface setLockMode, getLockMode;
79    private CommandInterface setQueryTimeout, getQueryTimeout;
80 
81    private int savepointId;
82    private String catalog;
83    private Statement executingStatement;
84    private final CompareMode compareMode = CompareMode.getInstance(null, 0);
85    private final CloseWatcher watcher;
86    private int queryTimeoutCache = -1;
87 
88    /**
89     * INTERNAL
90     */
91    public JdbcConnection(String url, Properties info) throws SQLException {
92        this(new ConnectionInfo(url, info), true);
93    }
94 
95    /**
96     * INTERNAL
97     */
98    public JdbcConnection(ConnectionInfo ci, boolean useBaseDir)
99            throws SQLException {
100        try {
101            if (useBaseDir) {
102                String baseDir = SysProperties.getBaseDir();
103                if (baseDir != null) {
104                    ci.setBaseDir(baseDir);
105                }
106            }
107            // this will return an embedded or server connection
108            session = new SessionRemote(ci).connectEmbeddedOrServer(false);
109            trace = session.getTrace();
110            int id = getNextId(TraceObject.CONNECTION);
111            setTrace(trace, TraceObject.CONNECTION, id);
112            this.user = ci.getUserName();
113            if (isInfoEnabled()) {
114                trace.infoCode("Connection " + getTraceObjectName()
115                        + " = DriverManager.getConnection(" + quote(ci.getOriginalURL())
116                        + ", " + quote(user) + ", \"\");");
117            }
118            this.url = ci.getURL();
119            closeOld();
120            watcher = CloseWatcher.register(this, session, keepOpenStackTrace);
121        } catch (Exception e) {
122            throw logAndConvert(e);
123        }
124    }
125 
126    /**
127     * INTERNAL
128     */
129    public JdbcConnection(JdbcConnection clone) {
130        this.session = clone.session;
131        trace = session.getTrace();
132        int id = getNextId(TraceObject.CONNECTION);
133        setTrace(trace, TraceObject.CONNECTION, id);
134        this.user = clone.user;
135        this.url = clone.url;
136        this.catalog = clone.catalog;
137        this.commit = clone.commit;
138        this.getGeneratedKeys = clone.getGeneratedKeys;
139        this.getLockMode = clone.getLockMode;
140        this.getQueryTimeout = clone.getQueryTimeout;
141        this.getReadOnly = clone.getReadOnly;
142        this.rollback = clone.rollback;
143        this.watcher = null;
144    }
145 
146    /**
147     * INTERNAL
148     */
149    public JdbcConnection(SessionInterface session, String user, String url) {
150        this.session = session;
151        trace = session.getTrace();
152        int id = getNextId(TraceObject.CONNECTION);
153        setTrace(trace, TraceObject.CONNECTION, id);
154        this.user = user;
155        this.url = url;
156        this.watcher = null;
157    }
158 
159    private void closeOld() {
160        while (true) {
161            CloseWatcher w = CloseWatcher.pollUnclosed();
162            if (w == null) {
163                break;
164            }
165            try {
166                w.getCloseable().close();
167            } catch (Exception e) {
168                trace.error(e, "closing session");
169            }
170            // there was an unclosed object -
171            // keep the stack trace from now on
172            keepOpenStackTrace = true;
173            String s = w.getOpenStackTrace();
174            Exception ex = DbException.get(ErrorCode.TRACE_CONNECTION_NOT_CLOSED);
175            trace.error(ex, s);
176        }
177    }
178 
179    /**
180     * Creates a new statement.
181     *
182     * @return the new statement
183     * @throws SQLException if the connection is closed
184     */
185    @Override
186    public Statement createStatement() throws SQLException {
187        try {
188            int id = getNextId(TraceObject.STATEMENT);
189            if (isDebugEnabled()) {
190                debugCodeAssign("Statement", TraceObject.STATEMENT, id, "createStatement()");
191            }
192            checkClosed();
193            return new JdbcStatement(this, id,
194                    ResultSet.TYPE_FORWARD_ONLY,
195                    Constants.DEFAULT_RESULT_SET_CONCURRENCY, false);
196        } catch (Exception e) {
197            throw logAndConvert(e);
198        }
199    }
200 
201    /**
202     * Creates a statement with the specified result set type and concurrency.
203     *
204     * @param resultSetType the result set type (ResultSet.TYPE_*)
205     * @param resultSetConcurrency the concurrency (ResultSet.CONCUR_*)
206     * @return the statement
207     * @throws SQLException
208     *             if the connection is closed or the result set type or
209     *             concurrency are not supported
210     */
211    @Override
212    public Statement createStatement(int resultSetType, int resultSetConcurrency)
213            throws SQLException {
214        try {
215            int id = getNextId(TraceObject.STATEMENT);
216            if (isDebugEnabled()) {
217                debugCodeAssign("Statement", TraceObject.STATEMENT, id,
218                        "createStatement(" + resultSetType + ", " + resultSetConcurrency + ")");
219            }
220            checkTypeConcurrency(resultSetType, resultSetConcurrency);
221            checkClosed();
222            return new JdbcStatement(this, id, resultSetType, resultSetConcurrency, false);
223        } catch (Exception e) {
224            throw logAndConvert(e);
225        }
226    }
227 
228    /**
229     * Creates a statement with the specified result set type, concurrency, and
230     * holdability.
231     *
232     * @param resultSetType the result set type (ResultSet.TYPE_*)
233     * @param resultSetConcurrency the concurrency (ResultSet.CONCUR_*)
234     * @param resultSetHoldability the holdability (ResultSet.HOLD* / CLOSE*)
235     * @return the statement
236     * @throws SQLException if the connection is closed or the result set type,
237     *             concurrency, or holdability are not supported
238     */
239    @Override
240    public Statement createStatement(int resultSetType,
241            int resultSetConcurrency, int resultSetHoldability)
242            throws SQLException {
243        try {
244            int id = getNextId(TraceObject.STATEMENT);
245            if (isDebugEnabled()) {
246                debugCodeAssign("Statement", TraceObject.STATEMENT, id,
247                        "createStatement(" + resultSetType + ", " +
248                        resultSetConcurrency + ", " + resultSetHoldability + ")");
249            }
250            checkTypeConcurrency(resultSetType, resultSetConcurrency);
251            checkHoldability(resultSetHoldability);
252            checkClosed();
253            return new JdbcStatement(this, id, resultSetType, resultSetConcurrency, false);
254        } catch (Exception e) {
255            throw logAndConvert(e);
256        }
257    }
258 
259    /**
260     * Creates a new prepared statement.
261     *
262     * @param sql the SQL statement
263     * @return the prepared statement
264     * @throws SQLException if the connection is closed
265     */
266    @Override
267    public PreparedStatement prepareStatement(String sql) throws SQLException {
268        try {
269            int id = getNextId(TraceObject.PREPARED_STATEMENT);
270            if (isDebugEnabled()) {
271                debugCodeAssign("PreparedStatement",
272                        TraceObject.PREPARED_STATEMENT, id,
273                        "prepareStatement(" + quote(sql) + ")");
274            }
275            checkClosed();
276            sql = translateSQL(sql);
277            return new JdbcPreparedStatement(this, sql, id,
278                    ResultSet.TYPE_FORWARD_ONLY,
279                    Constants.DEFAULT_RESULT_SET_CONCURRENCY, false);
280        } catch (Exception e) {
281            throw logAndConvert(e);
282        }
283    }
284 
285    /**
286     * Prepare a statement that will automatically close when the result set is
287     * closed. This method is used to retrieve database meta data.
288     *
289     * @param sql the SQL statement
290     * @return the prepared statement
291     */
292    PreparedStatement prepareAutoCloseStatement(String sql) throws SQLException {
293        try {
294            int id = getNextId(TraceObject.PREPARED_STATEMENT);
295            if (isDebugEnabled()) {
296                debugCodeAssign("PreparedStatement",
297                        TraceObject.PREPARED_STATEMENT, id,
298                        "prepareStatement(" + quote(sql) + ")");
299            }
300            checkClosed();
301            sql = translateSQL(sql);
302            return new JdbcPreparedStatement(this, sql, id,
303                    ResultSet.TYPE_FORWARD_ONLY,
304                    Constants.DEFAULT_RESULT_SET_CONCURRENCY, true);
305        } catch (Exception e) {
306            throw logAndConvert(e);
307        }
308    }
309 
310    /**
311     * Gets the database meta data for this database.
312     *
313     * @return the database meta data
314     * @throws SQLException if the connection is closed
315     */
316    @Override
317    public DatabaseMetaData getMetaData() throws SQLException {
318        try {
319            int id = getNextId(TraceObject.DATABASE_META_DATA);
320            if (isDebugEnabled()) {
321                debugCodeAssign("DatabaseMetaData",
322                        TraceObject.DATABASE_META_DATA, id, "getMetaData()");
323            }
324            checkClosed();
325            return new JdbcDatabaseMetaData(this, trace, id);
326        } catch (Exception e) {
327            throw logAndConvert(e);
328        }
329    }
330 
331    /**
332     * INTERNAL
333     */
334    public SessionInterface getSession() {
335        return session;
336    }
337 
338    /**
339     * Closes this connection. All open statements, prepared statements and
340     * result sets that where created by this connection become invalid after
341     * calling this method. If there is an uncommitted transaction, it will be
342     * rolled back.
343     */
344    @Override
345    public synchronized void close() throws SQLException {
346        try {
347            debugCodeCall("close");
348            if (session == null) {
349                return;
350            }
351            CloseWatcher.unregister(watcher);
352            session.cancel();
353            if (executingStatement != null) {
354                try {
355                    executingStatement.cancel();
356                } catch (NullPointerException e) {
357                    // ignore
358                }
359            }
360            synchronized (session) {
361                try {
362                    if (!session.isClosed()) {
363                        try {
364                            if (session.hasPendingTransaction()) {
365                                // roll back unless that would require to
366                                // re-connect (the transaction can't be rolled
367                                // back after re-connecting)
368                                if (!session.isReconnectNeeded(true)) {
369                                    try {
370                                        rollbackInternal();
371                                    } catch (DbException e) {
372                                        // ignore if the connection is broken
373                                        // right now
374                                        if (e.getErrorCode() !=
375                                                ErrorCode.CONNECTION_BROKEN_1) {
376                                            throw e;
377                                        }
378                                    }
379                                }
380                                session.afterWriting();
381                            }
382                            closePreparedCommands();
383                        } finally {
384                            session.close();
385                        }
386                    }
387                } finally {
388                    session = null;
389                }
390            }
391        } catch (Exception e) {
392            throw logAndConvert(e);
393        }
394    }
395 
396    private void closePreparedCommands() {
397        commit = closeAndSetNull(commit);
398        rollback = closeAndSetNull(rollback);
399        getReadOnly = closeAndSetNull(getReadOnly);
400        getGeneratedKeys = closeAndSetNull(getGeneratedKeys);
401        getLockMode = closeAndSetNull(getLockMode);
402        setLockMode = closeAndSetNull(setLockMode);
403        getQueryTimeout = closeAndSetNull(getQueryTimeout);
404        setQueryTimeout = closeAndSetNull(setQueryTimeout);
405    }
406 
407    private static CommandInterface closeAndSetNull(CommandInterface command) {
408        if (command != null) {
409            command.close();
410        }
411        return null;
412    }
413 
414    /**
415     * Switches auto commit on or off. Enabling it commits an uncommitted
416     * transaction, if there is one.
417     *
418     * @param autoCommit true for auto commit on, false for off
419     * @throws SQLException if the connection is closed
420     */
421    @Override
422    public synchronized void setAutoCommit(boolean autoCommit)
423            throws SQLException {
424        try {
425            if (isDebugEnabled()) {
426                debugCode("setAutoCommit(" + autoCommit + ");");
427            }
428            checkClosed();
429            if (autoCommit && !session.getAutoCommit()) {
430                commit();
431            }
432            session.setAutoCommit(autoCommit);
433        } catch (Exception e) {
434            throw logAndConvert(e);
435        }
436    }
437 
438    /**
439     * Gets the current setting for auto commit.
440     *
441     * @return true for on, false for off
442     * @throws SQLException if the connection is closed
443     */
444    @Override
445    public synchronized boolean getAutoCommit() throws SQLException {
446        try {
447            checkClosed();
448            debugCodeCall("getAutoCommit");
449            return session.getAutoCommit();
450        } catch (Exception e) {
451            throw logAndConvert(e);
452        }
453    }
454 
455    /**
456     * Commits the current transaction. This call has only an effect if auto
457     * commit is switched off.
458     *
459     * @throws SQLException if the connection is closed
460     */
461    @Override
462    public synchronized void commit() throws SQLException {
463        try {
464            debugCodeCall("commit");
465            checkClosedForWrite();
466            try {
467                commit = prepareCommand("COMMIT", commit);
468                commit.executeUpdate();
469            } finally {
470                afterWriting();
471            }
472        } catch (Exception e) {
473            throw logAndConvert(e);
474        }
475    }
476 
477    /**
478     * Rolls back the current transaction. This call has only an effect if auto
479     * commit is switched off.
480     *
481     * @throws SQLException if the connection is closed
482     */
483    @Override
484    public synchronized void rollback() throws SQLException {
485        try {
486            debugCodeCall("rollback");
487            checkClosedForWrite();
488            try {
489                rollbackInternal();
490            } finally {
491                afterWriting();
492            }
493        } catch (Exception e) {
494            throw logAndConvert(e);
495        }
496    }
497 
498    /**
499     * Returns true if this connection has been closed.
500     *
501     * @return true if close was called
502     */
503    @Override
504    public boolean isClosed() throws SQLException {
505        try {
506            debugCodeCall("isClosed");
507            return session == null || session.isClosed();
508        } catch (Exception e) {
509            throw logAndConvert(e);
510        }
511    }
512 
513    /**
514     * Translates a SQL statement into the database grammar.
515     *
516     * @param sql the SQL statement with or without JDBC escape sequences
517     * @return the translated statement
518     * @throws SQLException if the connection is closed
519     */
520    @Override
521    public String nativeSQL(String sql) throws SQLException {
522        try {
523            debugCodeCall("nativeSQL", sql);
524            checkClosed();
525            return translateSQL(sql);
526        } catch (Exception e) {
527            throw logAndConvert(e);
528        }
529    }
530 
531    /**
532     * According to the JDBC specs, this setting is only a hint to the database
533     * to enable optimizations - it does not cause writes to be prohibited.
534     *
535     * @param readOnly ignored
536     * @throws SQLException if the connection is closed
537     */
538    @Override
539    public void setReadOnly(boolean readOnly) throws SQLException {
540        try {
541            if (isDebugEnabled()) {
542                debugCode("setReadOnly(" + readOnly + ");");
543            }
544            checkClosed();
545        } catch (Exception e) {
546            throw logAndConvert(e);
547        }
548    }
549 
550    /**
551     * Returns true if the database is read-only.
552     *
553     * @return if the database is read-only
554     * @throws SQLException if the connection is closed
555     */
556    @Override
557    public boolean isReadOnly() throws SQLException {
558        try {
559            debugCodeCall("isReadOnly");
560            checkClosed();
561            getReadOnly = prepareCommand("CALL READONLY()", getReadOnly);
562            ResultInterface result = getReadOnly.executeQuery(0, false);
563            result.next();
564            boolean readOnly = result.currentRow()[0].getBoolean().booleanValue();
565            return readOnly;
566        } catch (Exception e) {
567            throw logAndConvert(e);
568        }
569    }
570 
571    /**
572     * Set the default catalog name. This call is ignored.
573     *
574     * @param catalog ignored
575     * @throws SQLException if the connection is closed
576     */
577    @Override
578    public void setCatalog(String catalog) throws SQLException {
579        try {
580            debugCodeCall("setCatalog", catalog);
581            checkClosed();
582        } catch (Exception e) {
583            throw logAndConvert(e);
584        }
585    }
586 
587    /**
588     * Gets the current catalog name.
589     *
590     * @return the catalog name
591     * @throws SQLException if the connection is closed
592     */
593    @Override
594    public String getCatalog() throws SQLException {
595        try {
596            debugCodeCall("getCatalog");
597            checkClosed();
598            if (catalog == null) {
599                CommandInterface cat = prepareCommand("CALL DATABASE()", Integer.MAX_VALUE);
600                ResultInterface result = cat.executeQuery(0, false);
601                result.next();
602                catalog = result.currentRow()[0].getString();
603                cat.close();
604            }
605            return catalog;
606        } catch (Exception e) {
607            throw logAndConvert(e);
608        }
609    }
610 
611    /**
612     * Gets the first warning reported by calls on this object.
613     *
614     * @return null
615     */
616    @Override
617    public SQLWarning getWarnings() throws SQLException {
618        try {
619            debugCodeCall("getWarnings");
620            checkClosed();
621            return null;
622        } catch (Exception e) {
623            throw logAndConvert(e);
624        }
625    }
626 
627    /**
628     * Clears all warnings.
629     */
630    @Override
631    public void clearWarnings() throws SQLException {
632        try {
633            debugCodeCall("clearWarnings");
634            checkClosed();
635        } catch (Exception e) {
636            throw logAndConvert(e);
637        }
638    }
639 
640    /**
641     * Creates a prepared statement with the specified result set type and
642     * concurrency.
643     *
644     * @param sql the SQL statement
645     * @param resultSetType the result set type (ResultSet.TYPE_*)
646     * @param resultSetConcurrency the concurrency (ResultSet.CONCUR_*)
647     * @return the prepared statement
648     * @throws SQLException
649     *             if the connection is closed or the result set type or
650     *             concurrency are not supported
651     */
652    @Override
653    public PreparedStatement prepareStatement(String sql, int resultSetType,
654            int resultSetConcurrency) throws SQLException {
655        try {
656            int id = getNextId(TraceObject.PREPARED_STATEMENT);
657            if (isDebugEnabled()) {
658                debugCodeAssign("PreparedStatement", TraceObject.PREPARED_STATEMENT, id,
659                        "prepareStatement(" + quote(sql) + ", " +
660                                resultSetType + ", " + resultSetConcurrency +
661                                ")");
662            }
663            checkTypeConcurrency(resultSetType, resultSetConcurrency);
664            checkClosed();
665            sql = translateSQL(sql);
666            return new JdbcPreparedStatement(this, sql, id, resultSetType,
667                    resultSetConcurrency, false);
668        } catch (Exception e) {
669            throw logAndConvert(e);
670        }
671    }
672 
673    /**
674     * Changes the current transaction isolation level. Calling this method will
675     * commit an open transaction, even if the new level is the same as the old
676     * one, except if the level is not supported. Internally, this method calls
677     * SET LOCK_MODE, which affects all connections.
678     * The following isolation levels are supported:
679     * <ul>
680     * <li> Connection.TRANSACTION_READ_UNCOMMITTED = SET LOCK_MODE 0: no
681     * locking (should only be used for testing). </li>
682     * <li>Connection.TRANSACTION_SERIALIZABLE = SET LOCK_MODE 1: table level
683     * locking. </li>
684     * <li>Connection.TRANSACTION_READ_COMMITTED = SET LOCK_MODE 3: table
685     * level locking, but read locks are released immediately (default). </li>
686     * </ul>
687     * This setting is not persistent. Please note that using
688     * TRANSACTION_READ_UNCOMMITTED while at the same time using multiple
689     * connections may result in inconsistent transactions.
690     *
691     * @param level the new transaction isolation level:
692     *            Connection.TRANSACTION_READ_UNCOMMITTED,
693     *            Connection.TRANSACTION_READ_COMMITTED, or
694     *            Connection.TRANSACTION_SERIALIZABLE
695     * @throws SQLException if the connection is closed or the isolation level
696     *             is not supported
697     */
698    @Override
699    public void setTransactionIsolation(int level) throws SQLException {
700        try {
701            debugCodeCall("setTransactionIsolation", level);
702            checkClosed();
703            int lockMode;
704            switch(level) {
705            case Connection.TRANSACTION_READ_UNCOMMITTED:
706                lockMode = Constants.LOCK_MODE_OFF;
707                break;
708            case Connection.TRANSACTION_READ_COMMITTED:
709                lockMode = Constants.LOCK_MODE_READ_COMMITTED;
710                break;
711            case Connection.TRANSACTION_REPEATABLE_READ:
712            case Connection.TRANSACTION_SERIALIZABLE:
713                lockMode = Constants.LOCK_MODE_TABLE;
714                break;
715            default:
716                throw DbException.getInvalidValueException("level", level);
717            }
718            commit();
719            setLockMode = prepareCommand("SET LOCK_MODE ?", setLockMode);
720            setLockMode.getParameters().get(0).setValue(ValueInt.get(lockMode), false);
721            setLockMode.executeUpdate();
722        } catch (Exception e) {
723            throw logAndConvert(e);
724        }
725    }
726 
727    /**
728     * INTERNAL
729     */
730    public void setQueryTimeout(int seconds) throws SQLException {
731        try {
732            debugCodeCall("setQueryTimeout", seconds);
733            checkClosed();
734            setQueryTimeout = prepareCommand("SET QUERY_TIMEOUT ?", setQueryTimeout);
735            setQueryTimeout.getParameters().get(0).
736                    setValue(ValueInt.get(seconds * 1000), false);
737            setQueryTimeout.executeUpdate();
738            queryTimeoutCache = seconds;
739        } catch (Exception e) {
740            throw logAndConvert(e);
741        }
742    }
743 
744    /**
745     * INTERNAL
746     */
747    int getQueryTimeout() throws SQLException {
748        try {
749            if (queryTimeoutCache == -1) {
750                checkClosed();
751                getQueryTimeout = prepareCommand(
752                        "SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS " +
753                        "WHERE NAME=?", getQueryTimeout);
754                getQueryTimeout.getParameters().get(0).
755                        setValue(ValueString.get("QUERY_TIMEOUT"), false);
756                ResultInterface result = getQueryTimeout.executeQuery(0, false);
757                result.next();
758                int queryTimeout = result.currentRow()[0].getInt();
759                result.close();
760                if (queryTimeout != 0) {
761                    // round to the next second, otherwise 999 millis would
762                    // return 0 seconds
763                    queryTimeout = (queryTimeout + 999) / 1000;
764                }
765                queryTimeoutCache = queryTimeout;
766            }
767            return queryTimeoutCache;
768        } catch (Exception e) {
769            throw logAndConvert(e);
770        }
771    }
772 
773    /**
774     * Returns the current transaction isolation level.
775     *
776     * @return the isolation level.
777     * @throws SQLException if the connection is closed
778     */
779    @Override
780    public int getTransactionIsolation() throws SQLException {
781        try {
782            debugCodeCall("getTransactionIsolation");
783            checkClosed();
784            getLockMode = prepareCommand("CALL LOCK_MODE()", getLockMode);
785            ResultInterface result = getLockMode.executeQuery(0, false);
786            result.next();
787            int lockMode = result.currentRow()[0].getInt();
788            result.close();
789            int transactionIsolationLevel;
790            switch(lockMode) {
791            case Constants.LOCK_MODE_OFF:
792                transactionIsolationLevel = Connection.TRANSACTION_READ_UNCOMMITTED;
793                break;
794            case Constants.LOCK_MODE_READ_COMMITTED:
795                transactionIsolationLevel = Connection.TRANSACTION_READ_COMMITTED;
796                break;
797            case Constants.LOCK_MODE_TABLE:
798            case Constants.LOCK_MODE_TABLE_GC:
799                transactionIsolationLevel = Connection.TRANSACTION_SERIALIZABLE;
800                break;
801            default:
802                throw DbException.throwInternalError("lockMode:" + lockMode);
803            }
804            return transactionIsolationLevel;
805        } catch (Exception e) {
806            throw logAndConvert(e);
807        }
808    }
809 
810    /**
811     * Changes the current result set holdability.
812     *
813     * @param holdability
814     *            ResultSet.HOLD_CURSORS_OVER_COMMIT or
815     *            ResultSet.CLOSE_CURSORS_AT_COMMIT;
816     * @throws SQLException
817     *            if the connection is closed or the holdability is not
818     *            supported
819     */
820    @Override
821    public void setHoldability(int holdability) throws SQLException {
822        try {
823            debugCodeCall("setHoldability", holdability);
824            checkClosed();
825            checkHoldability(holdability);
826            this.holdability = holdability;
827        } catch (Exception e) {
828            throw logAndConvert(e);
829        }
830    }
831 
832    /**
833     * Returns the current result set holdability.
834     *
835     * @return the holdability
836     * @throws SQLException if the connection is closed
837     */
838    @Override
839    public int getHoldability() throws SQLException {
840        try {
841            debugCodeCall("getHoldability");
842            checkClosed();
843            return holdability;
844        } catch (Exception e) {
845            throw logAndConvert(e);
846        }
847    }
848 
849    /**
850     * Gets the type map.
851     *
852     * @return null
853     * @throws SQLException if the connection is closed
854     */
855    @Override
856    public Map<String, Class<?>> getTypeMap() throws SQLException {
857        try {
858            debugCodeCall("getTypeMap");
859            checkClosed();
860            return null;
861        } catch (Exception e) {
862            throw logAndConvert(e);
863        }
864    }
865 
866    /**
867     * [Partially supported] Sets the type map. This is only supported if the
868     * map is empty or null.
869     */
870    @Override
871    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
872        try {
873            debugCode("setTypeMap(" + quoteMap(map) + ");");
874            checkMap(map);
875        } catch (Exception e) {
876            throw logAndConvert(e);
877        }
878    }
879 
880    /**
881     * Creates a new callable statement.
882     *
883     * @param sql the SQL statement
884     * @return the callable statement
885     * @throws SQLException
886     *             if the connection is closed or the statement is not valid
887     */
888    @Override
889    public CallableStatement prepareCall(String sql) throws SQLException {
890        try {
891            int id = getNextId(TraceObject.CALLABLE_STATEMENT);
892            if (isDebugEnabled()) {
893                debugCodeAssign("CallableStatement",
894                        TraceObject.CALLABLE_STATEMENT, id, "prepareCall(" +
895                                quote(sql) + ")");
896            }
897            checkClosed();
898            sql = translateSQL(sql);
899            return new JdbcCallableStatement(this, sql, id,
900                    ResultSet.TYPE_FORWARD_ONLY,
901                    Constants.DEFAULT_RESULT_SET_CONCURRENCY);
902        } catch (Exception e) {
903            throw logAndConvert(e);
904        }
905    }
906 
907    /**
908     * Creates a callable statement with the specified result set type and
909     * concurrency.
910     *
911     * @param sql the SQL statement
912     * @param resultSetType the result set type (ResultSet.TYPE_*)
913     * @param resultSetConcurrency the concurrency (ResultSet.CONCUR_*)
914     * @return the callable statement
915     * @throws SQLException
916     *             if the connection is closed or the result set type or
917     *             concurrency are not supported
918     */
919    @Override
920    public CallableStatement prepareCall(String sql, int resultSetType,
921            int resultSetConcurrency) throws SQLException {
922        try {
923            int id = getNextId(TraceObject.CALLABLE_STATEMENT);
924            if (isDebugEnabled()) {
925                debugCodeAssign("CallableStatement",
926                        TraceObject.CALLABLE_STATEMENT, id, "prepareCall(" +
927                                quote(sql) + ", " + resultSetType + ", " +
928                                resultSetConcurrency + ")");
929            }
930            checkTypeConcurrency(resultSetType, resultSetConcurrency);
931            checkClosed();
932            sql = translateSQL(sql);
933            return new JdbcCallableStatement(this, sql, id, resultSetType,
934                    resultSetConcurrency);
935        } catch (Exception e) {
936            throw logAndConvert(e);
937        }
938    }
939 
940    /**
941     * Creates a callable statement with the specified result set type,
942     * concurrency, and holdability.
943     *
944     * @param sql the SQL statement
945     * @param resultSetType the result set type (ResultSet.TYPE_*)
946     * @param resultSetConcurrency the concurrency (ResultSet.CONCUR_*)
947     * @param resultSetHoldability the holdability (ResultSet.HOLD* / CLOSE*)
948     * @return the callable statement
949     * @throws SQLException
950     *             if the connection is closed or the result set type,
951     *             concurrency, or holdability are not supported
952     */
953    @Override
954    public CallableStatement prepareCall(String sql, int resultSetType,
955            int resultSetConcurrency, int resultSetHoldability) throws SQLException {
956        try {
957            int id = getNextId(TraceObject.CALLABLE_STATEMENT);
958            if (isDebugEnabled()) {
959                debugCodeAssign("CallableStatement",
960                        TraceObject.CALLABLE_STATEMENT, id, "prepareCall(" +
961                                quote(sql) + ", " + resultSetType + ", " +
962                                resultSetConcurrency + ", " +
963                                resultSetHoldability + ")");
964            }
965            checkTypeConcurrency(resultSetType, resultSetConcurrency);
966            checkHoldability(resultSetHoldability);
967            checkClosed();
968            sql = translateSQL(sql);
969            return new JdbcCallableStatement(this, sql, id, resultSetType,
970                    resultSetConcurrency);
971        } catch (Exception e) {
972            throw logAndConvert(e);
973        }
974    }
975 
976    /**
977     * Creates a new unnamed savepoint.
978     *
979     * @return the new savepoint
980     */
981    @Override
982    public Savepoint setSavepoint() throws SQLException {
983        try {
984            int id = getNextId(TraceObject.SAVEPOINT);
985            if (isDebugEnabled()) {
986                debugCodeAssign("Savepoint", TraceObject.SAVEPOINT, id, "setSavepoint()");
987            }
988            checkClosed();
989            CommandInterface set = prepareCommand(
990                    "SAVEPOINT " + JdbcSavepoint.getName(null, savepointId),
991                    Integer.MAX_VALUE);
992            set.executeUpdate();
993            JdbcSavepoint savepoint = new JdbcSavepoint(this, savepointId, null, trace, id);
994            savepointId++;
995            return savepoint;
996        } catch (Exception e) {
997            throw logAndConvert(e);
998        }
999    }
1000 
1001    /**
1002     * Creates a new named savepoint.
1003     *
1004     * @param name the savepoint name
1005     * @return the new savepoint
1006     */
1007    @Override
1008    public Savepoint setSavepoint(String name) throws SQLException {
1009        try {
1010            int id = getNextId(TraceObject.SAVEPOINT);
1011            if (isDebugEnabled()) {
1012                debugCodeAssign("Savepoint",
1013                        TraceObject.SAVEPOINT, id, "setSavepoint(" + quote(name) + ")");
1014            }
1015            checkClosed();
1016            CommandInterface set = prepareCommand(
1017                    "SAVEPOINT " + JdbcSavepoint.getName(name, 0), Integer.MAX_VALUE);
1018            set.executeUpdate();
1019            JdbcSavepoint savepoint = new JdbcSavepoint(this, 0, name, trace, id);
1020            return savepoint;
1021        } catch (Exception e) {
1022            throw logAndConvert(e);
1023        }
1024    }
1025 
1026    /**
1027     * Rolls back to a savepoint.
1028     *
1029     * @param savepoint the savepoint
1030     */
1031    @Override
1032    public void rollback(Savepoint savepoint) throws SQLException {
1033        try {
1034            JdbcSavepoint sp = convertSavepoint(savepoint);
1035            debugCode("rollback(" + sp.getTraceObjectName() + ");");
1036            checkClosedForWrite();
1037            try {
1038                sp.rollback();
1039            } finally {
1040                afterWriting();
1041            }
1042        } catch (Exception e) {
1043            throw logAndConvert(e);
1044        }
1045    }
1046 
1047    /**
1048     * Releases a savepoint.
1049     *
1050     * @param savepoint the savepoint to release
1051     */
1052    @Override
1053    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
1054        try {
1055            debugCode("releaseSavepoint(savepoint);");
1056            checkClosed();
1057            convertSavepoint(savepoint).release();
1058        } catch (Exception e) {
1059            throw logAndConvert(e);
1060        }
1061    }
1062 
1063    private static JdbcSavepoint convertSavepoint(Savepoint savepoint) {
1064        if (!(savepoint instanceof JdbcSavepoint)) {
1065            throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, "" + savepoint);
1066        }
1067        return (JdbcSavepoint) savepoint;
1068    }
1069 
1070    /**
1071     * Creates a prepared statement with the specified result set type,
1072     * concurrency, and holdability.
1073     *
1074     * @param sql the SQL statement
1075     * @param resultSetType the result set type (ResultSet.TYPE_*)
1076     * @param resultSetConcurrency the concurrency (ResultSet.CONCUR_*)
1077     * @param resultSetHoldability the holdability (ResultSet.HOLD* / CLOSE*)
1078     * @return the prepared statement
1079     * @throws SQLException if the connection is closed or the result set type,
1080     *             concurrency, or holdability are not supported
1081     */
1082    @Override
1083    public PreparedStatement prepareStatement(String sql, int resultSetType,
1084            int resultSetConcurrency, int resultSetHoldability)
1085            throws SQLException {
1086        try {
1087            int id = getNextId(TraceObject.PREPARED_STATEMENT);
1088            if (isDebugEnabled()) {
1089                debugCodeAssign("PreparedStatement", TraceObject.PREPARED_STATEMENT, id,
1090                        "prepareStatement(" + quote(sql) + ", " +
1091                        resultSetType + ", " + resultSetConcurrency + ", " +
1092                        resultSetHoldability + ")");
1093            }
1094            checkTypeConcurrency(resultSetType, resultSetConcurrency);
1095            checkHoldability(resultSetHoldability);
1096            checkClosed();
1097            sql = translateSQL(sql);
1098            return new JdbcPreparedStatement(this, sql, id,
1099                    resultSetType, resultSetConcurrency, false);
1100        } catch (Exception e) {
1101            throw logAndConvert(e);
1102        }
1103    }
1104 
1105    /**
1106     * Creates a new prepared statement.
1107     * This method just calls prepareStatement(String sql) internally.
1108     * The method getGeneratedKeys only supports one column.
1109     *
1110     * @param sql the SQL statement
1111     * @param autoGeneratedKeys ignored
1112     * @return the prepared statement
1113     * @throws SQLException
1114     *             if the connection is closed
1115     */
1116    @Override
1117    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
1118            throws SQLException {
1119        try {
1120            if (isDebugEnabled()) {
1121                debugCode("prepareStatement(" + quote(sql) + ", " + autoGeneratedKeys + ");");
1122            }
1123            return prepareStatement(sql);
1124        } catch (Exception e) {
1125            throw logAndConvert(e);
1126        }
1127    }
1128 
1129    /**
1130     * Creates a new prepared statement.
1131     * This method just calls prepareStatement(String sql) internally.
1132     * The method getGeneratedKeys only supports one column.
1133     *
1134     * @param sql the SQL statement
1135     * @param columnIndexes ignored
1136     * @return the prepared statement
1137     * @throws SQLException
1138     *             if the connection is closed
1139     */
1140    @Override
1141    public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
1142            throws SQLException {
1143        try {
1144            if (isDebugEnabled()) {
1145                debugCode("prepareStatement(" + quote(sql) + ", " +
1146                        quoteIntArray(columnIndexes) + ");");
1147            }
1148            return prepareStatement(sql);
1149        } catch (Exception e) {
1150            throw logAndConvert(e);
1151        }
1152    }
1153 
1154    /**
1155     * Creates a new prepared statement.
1156     * This method just calls prepareStatement(String sql) internally.
1157     * The method getGeneratedKeys only supports one column.
1158     *
1159     * @param sql the SQL statement
1160     * @param columnNames ignored
1161     * @return the prepared statement
1162     * @throws SQLException
1163     *             if the connection is closed
1164     */
1165    @Override
1166    public PreparedStatement prepareStatement(String sql, String[] columnNames)
1167            throws SQLException {
1168        try {
1169            if (isDebugEnabled()) {
1170                debugCode("prepareStatement(" + quote(sql) + ", " +
1171                        quoteArray(columnNames) + ");");
1172            }
1173            return prepareStatement(sql);
1174        } catch (Exception e) {
1175            throw logAndConvert(e);
1176        }
1177    }
1178 
1179    // =============================================================
1180 
1181    /**
1182     * Prepare an command. This will parse the SQL statement.
1183     *
1184     * @param sql the SQL statement
1185     * @param fetchSize the fetch size (used in remote connections)
1186     * @return the command
1187     */
1188    CommandInterface prepareCommand(String sql, int fetchSize) {
1189        return session.prepareCommand(sql, fetchSize);
1190    }
1191 
1192    private CommandInterface prepareCommand(String sql, CommandInterface old) {
1193        return old == null ? session.prepareCommand(sql, Integer.MAX_VALUE) : old;
1194    }
1195 
1196    private static int translateGetEnd(String sql, int i, char c) {
1197        int len = sql.length();
1198        switch(c) {
1199        case '$': {
1200            if (i < len - 1 && sql.charAt(i + 1) == '$' &&
1201                    (i == 0 || sql.charAt(i - 1) <= ' ')) {
1202                int j = sql.indexOf("$$", i + 2);
1203                if (j < 0) {
1204                    throw DbException.getSyntaxError(sql, i);
1205                }
1206                return j + 1;
1207            }
1208            return i;
1209        }
1210        case '\'': {
1211            int j = sql.indexOf('\'', i + 1);
1212            if (j < 0) {
1213                throw DbException.getSyntaxError(sql, i);
1214            }
1215            return j;
1216        }
1217        case '"': {
1218            int j = sql.indexOf('"', i + 1);
1219            if (j < 0) {
1220                throw DbException.getSyntaxError(sql, i);
1221            }
1222            return j;
1223        }
1224        case '/': {
1225            checkRunOver(i+1, len, sql);
1226            if (sql.charAt(i + 1) == '*') {
1227                // block comment
1228                int j = sql.indexOf("*/", i + 2);
1229                if (j < 0) {
1230                    throw DbException.getSyntaxError(sql, i);
1231                }
1232                i = j + 1;
1233            } else if (sql.charAt(i + 1) == '/') {
1234                // single line comment
1235                i += 2;
1236                while (i < len && (c = sql.charAt(i)) != '\r' && c != '\n') {
1237                    i++;
1238                }
1239            }
1240            return i;
1241        }
1242        case '-': {
1243            checkRunOver(i+1, len, sql);
1244            if (sql.charAt(i + 1) == '-') {
1245                // single line comment
1246                i += 2;
1247                while (i < len && (c = sql.charAt(i)) != '\r' && c != '\n') {
1248                    i++;
1249                }
1250            }
1251            return i;
1252        }
1253        default:
1254            throw DbException.throwInternalError("c=" + c);
1255        }
1256    }
1257 
1258    /**
1259     * Convert JDBC escape sequences in the SQL statement. This
1260     * method throws an exception if the SQL statement is null.
1261     *
1262     * @param sql the SQL statement with or without JDBC escape sequences
1263     * @return the SQL statement without JDBC escape sequences
1264     */
1265    private static String translateSQL(String sql) {
1266        return translateSQL(sql, true);
1267    }
1268 
1269    /**
1270     * Convert JDBC escape sequences in the SQL statement if required. This
1271     * method throws an exception if the SQL statement is null.
1272     *
1273     * @param sql the SQL statement with or without JDBC escape sequences
1274     * @param escapeProcessing whether escape sequences should be replaced
1275     * @return the SQL statement without JDBC escape sequences
1276     */
1277    static String translateSQL(String sql, boolean escapeProcessing) {
1278        if (sql == null) {
1279            throw DbException.getInvalidValueException("SQL", null);
1280        }
1281        if (!escapeProcessing) {
1282            return sql;
1283        }
1284        if (sql.indexOf('{') < 0) {
1285            return sql;
1286        }
1287        int len = sql.length();
1288        char[] chars = null;
1289        int level = 0;
1290        for (int i = 0; i < len; i++) {
1291            char c = sql.charAt(i);
1292            switch (c) {
1293            case '\'':
1294            case '"':
1295            case '/':
1296            case '-':
1297                i = translateGetEnd(sql, i, c);
1298                break;
1299            case '{':
1300                level++;
1301                if (chars == null) {
1302                    chars = sql.toCharArray();
1303                }
1304                chars[i] = ' ';
1305                while (Character.isSpaceChar(chars[i])) {
1306                    i++;
1307                    checkRunOver(i, len, sql);
1308                }
1309                int start = i;
1310                if (chars[i] >= '0' && chars[i] <= '9') {
1311                    chars[i - 1] = '{';
1312                    while (true) {
1313                        checkRunOver(i, len, sql);
1314                        c = chars[i];
1315                        if (c == '}') {
1316                            break;
1317                        }
1318                        switch (c) {
1319                        case '\'':
1320                        case '"':
1321                        case '/':
1322                        case '-':
1323                            i = translateGetEnd(sql, i, c);
1324                            break;
1325                        default:
1326                        }
1327                        i++;
1328                    }
1329                    level--;
1330                    break;
1331                } else if (chars[i] == '?') {
1332                    i++;
1333                    checkRunOver(i, len, sql);
1334                    while (Character.isSpaceChar(chars[i])) {
1335                        i++;
1336                        checkRunOver(i, len, sql);
1337                    }
1338                    if (sql.charAt(i) != '=') {
1339                        throw DbException.getSyntaxError(sql, i, "=");
1340                    }
1341                    i++;
1342                    checkRunOver(i, len, sql);
1343                    while (Character.isSpaceChar(chars[i])) {
1344                        i++;
1345                        checkRunOver(i, len, sql);
1346                    }
1347                }
1348                while (!Character.isSpaceChar(chars[i])) {
1349                    i++;
1350                    checkRunOver(i, len, sql);
1351                }
1352                int remove = 0;
1353                if (found(sql, start, "fn")) {
1354                    remove = 2;
1355                } else if (found(sql, start, "escape")) {
1356                    break;
1357                } else if (found(sql, start, "call")) {
1358                    break;
1359                } else if (found(sql, start, "oj")) {
1360                    remove = 2;
1361                } else if (found(sql, start, "ts")) {
1362                    break;
1363                } else if (found(sql, start, "t")) {
1364                    break;
1365                } else if (found(sql, start, "d")) {
1366                    break;
1367                } else if (found(sql, start, "params")) {
1368                    remove = "params".length();
1369                }
1370                for (i = start; remove > 0; i++, remove--) {
1371                    chars[i] = ' ';
1372                }
1373                break;
1374            case '}':
1375                if (--level < 0) {
1376                    throw DbException.getSyntaxError(sql, i);
1377                }
1378                chars[i] = ' ';
1379                break;
1380            case '$':
1381                i = translateGetEnd(sql, i, c);
1382                break;
1383            default:
1384            }
1385        }
1386        if (level != 0) {
1387            throw DbException.getSyntaxError(sql, sql.length() - 1);
1388        }
1389        if (chars != null) {
1390            sql = new String(chars);
1391        }
1392        return sql;
1393    }
1394 
1395    private static void checkRunOver(int i, int len, String sql) {
1396        if (i >= len) {
1397            throw DbException.getSyntaxError(sql, i);
1398        }
1399    }
1400 
1401    private static boolean found(String sql, int start, String other) {
1402        return sql.regionMatches(true, start, other, 0, other.length());
1403    }
1404 
1405    private static void checkTypeConcurrency(int resultSetType,
1406            int resultSetConcurrency) {
1407        switch (resultSetType) {
1408        case ResultSet.TYPE_FORWARD_ONLY:
1409        case ResultSet.TYPE_SCROLL_INSENSITIVE:
1410        case ResultSet.TYPE_SCROLL_SENSITIVE:
1411            break;
1412        default:
1413            throw DbException.getInvalidValueException("resultSetType",
1414                    resultSetType);
1415        }
1416        switch (resultSetConcurrency) {
1417        case ResultSet.CONCUR_READ_ONLY:
1418        case ResultSet.CONCUR_UPDATABLE:
1419            break;
1420        default:
1421            throw DbException.getInvalidValueException("resultSetConcurrency",
1422                    resultSetConcurrency);
1423        }
1424    }
1425 
1426    private static void checkHoldability(int resultSetHoldability) {
1427        // TODO compatibility / correctness: DBPool uses
1428        // ResultSet.HOLD_CURSORS_OVER_COMMIT
1429        if (resultSetHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT
1430                && resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) {
1431            throw DbException.getInvalidValueException("resultSetHoldability",
1432                    resultSetHoldability);
1433        }
1434    }
1435 
1436    /**
1437     * INTERNAL.
1438     * Check if this connection is closed.
1439     * The next operation is a read request.
1440     *
1441     * @throws DbException if the connection or session is closed
1442     */
1443    protected void checkClosed() {
1444        checkClosed(false);
1445    }
1446 
1447    /**
1448     * Check if this connection is closed.
1449     * The next operation may be a write request.
1450     *
1451     * @throws DbException if the connection or session is closed
1452     */
1453    private void checkClosedForWrite() {
1454        checkClosed(true);
1455    }
1456 
1457    /**
1458     * INTERNAL.
1459     * Check if this connection is closed.
1460     *
1461     * @param write if the next operation is possibly writing
1462     * @throws DbException if the connection or session is closed
1463     */
1464    protected void checkClosed(boolean write) {
1465        if (session == null) {
1466            throw DbException.get(ErrorCode.OBJECT_CLOSED);
1467        }
1468        if (session.isClosed()) {
1469            throw DbException.get(ErrorCode.DATABASE_CALLED_AT_SHUTDOWN);
1470        }
1471        if (session.isReconnectNeeded(write)) {
1472            trace.debug("reconnect");
1473            closePreparedCommands();
1474            session = session.reconnect(write);
1475            trace = session.getTrace();
1476        }
1477    }
1478 
1479    /**
1480     * INTERNAL.
1481     * Called after executing a command that could have written something.
1482     */
1483    protected void afterWriting() {
1484        if (session != null) {
1485            session.afterWriting();
1486        }
1487    }
1488 
1489    String getURL() {
1490        checkClosed();
1491        return url;
1492    }
1493 
1494    String getUser() {
1495        checkClosed();
1496        return user;
1497    }
1498 
1499    private void rollbackInternal() {
1500        rollback = prepareCommand("ROLLBACK", rollback);
1501        rollback.executeUpdate();
1502    }
1503 
1504    /**
1505     * INTERNAL
1506     */
1507    public int getPowerOffCount() {
1508        return (session == null || session.isClosed()) ?
1509                0 : session.getPowerOffCount();
1510    }
1511 
1512    /**
1513     * INTERNAL
1514     */
1515    public void setPowerOffCount(int count) {
1516        if (session != null) {
1517            session.setPowerOffCount(count);
1518        }
1519    }
1520 
1521    /**
1522     * INTERNAL
1523     */
1524    public void setExecutingStatement(Statement stat) {
1525        executingStatement = stat;
1526    }
1527 
1528    /**
1529     * INTERNAL
1530     */
1531    ResultSet getGeneratedKeys(JdbcStatement stat, int id) {
1532        getGeneratedKeys = prepareCommand(
1533                "SELECT SCOPE_IDENTITY() " +
1534                "WHERE SCOPE_IDENTITY() IS NOT NULL", getGeneratedKeys);
1535        ResultInterface result = getGeneratedKeys.executeQuery(0, false);
1536        ResultSet rs = new JdbcResultSet(this, stat, result, id, false, true, false);
1537        return rs;
1538    }
1539 
1540    /**
1541     * Create a new empty Clob object.
1542     *
1543     * @return the object
1544     */
1545    @Override
1546    public Clob createClob() throws SQLException {
1547        try {
1548            int id = getNextId(TraceObject.CLOB);
1549            debugCodeAssign("Clob", TraceObject.CLOB, id, "createClob()");
1550            checkClosedForWrite();
1551            try {
1552                Value v = session.getDataHandler().getLobStorage().createClob(
1553                        new InputStreamReader(
1554                        new ByteArrayInputStream(Utils.EMPTY_BYTES)), 0);
1555                session.addTemporaryLob(v);
1556                return new JdbcClob(this, v, id);
1557            } finally {
1558                afterWriting();
1559            }
1560        } catch (Exception e) {
1561            throw logAndConvert(e);
1562        }
1563    }
1564 
1565    /**
1566     * Create a new empty Blob object.
1567     *
1568     * @return the object
1569     */
1570    @Override
1571    public Blob createBlob() throws SQLException {
1572        try {
1573            int id = getNextId(TraceObject.BLOB);
1574            debugCodeAssign("Blob", TraceObject.BLOB, id, "createClob()");
1575            checkClosedForWrite();
1576            try {
1577                Value v = session.getDataHandler().getLobStorage().createBlob(
1578                        new ByteArrayInputStream(Utils.EMPTY_BYTES), 0);
1579                session.addTemporaryLob(v);
1580                return new JdbcBlob(this, v, id);
1581            } finally {
1582                afterWriting();
1583            }
1584        } catch (Exception e) {
1585            throw logAndConvert(e);
1586        }
1587    }
1588 
1589    /**
1590     * Create a new empty NClob object.
1591     *
1592     * @return the object
1593     */
1594    @Override
1595    public NClob createNClob() throws SQLException {
1596        try {
1597            int id = getNextId(TraceObject.CLOB);
1598            debugCodeAssign("NClob", TraceObject.CLOB, id, "createNClob()");
1599            checkClosedForWrite();
1600            try {
1601                Value v = session.getDataHandler().getLobStorage().createClob(
1602                        new InputStreamReader(
1603                        new ByteArrayInputStream(Utils.EMPTY_BYTES)), 0);
1604                session.addTemporaryLob(v);
1605                return new JdbcClob(this, v, id);
1606            } finally {
1607                afterWriting();
1608            }
1609        } catch (Exception e) {
1610            throw logAndConvert(e);
1611        }
1612    }
1613 
1614    /**
1615     * [Not supported] Create a new empty SQLXML object.
1616     */
1617    @Override
1618    public SQLXML createSQLXML() throws SQLException {
1619        throw unsupported("SQLXML");
1620    }
1621 
1622    /**
1623     * [Not supported] Create a new empty Array object.
1624     */
1625    @Override
1626    public Array createArrayOf(String typeName, Object[] elements)
1627            throws SQLException {
1628        throw unsupported("createArray");
1629    }
1630 
1631    /**
1632     * [Not supported] Create a new empty Struct object.
1633     */
1634    @Override
1635    public Struct createStruct(String typeName, Object[] attributes)
1636            throws SQLException {
1637        throw unsupported("Struct");
1638    }
1639 
1640    /**
1641     * Returns true if this connection is still valid.
1642     *
1643     * @param timeout the number of seconds to wait for the database to respond
1644     *            (ignored)
1645     * @return true if the connection is valid.
1646     */
1647    @Override
1648    public synchronized boolean isValid(int timeout) {
1649        try {
1650            debugCodeCall("isValid", timeout);
1651            if (session == null || session.isClosed()) {
1652                return false;
1653            }
1654            // force a network round trip (if networked)
1655            getTransactionIsolation();
1656            return true;
1657        } catch (Exception e) {
1658            // this method doesn't throw an exception, but it logs it
1659            logAndConvert(e);
1660            return false;
1661        }
1662    }
1663 
1664    /**
1665     * Set a client property.
1666     * This method always throws a SQLClientInfoException.
1667     *
1668     * @param name the name of the property (ignored)
1669     * @param value the value (ignored)
1670     */
1671    @Override
1672    public void setClientInfo(String name, String value)
1673            throws SQLClientInfoException {
1674        try {
1675            if (isDebugEnabled()) {
1676                debugCode("setClientInfo("
1677                        +quote(name)+", "
1678                        +quote(value)+");");
1679            }
1680            checkClosed();
1681            // we don't have any client properties, so just throw
1682            throw new SQLClientInfoException();
1683        } catch (Exception e) {
1684            throw convertToClientInfoException(logAndConvert(e));
1685        }
1686    }
1687 
1688    private static SQLClientInfoException convertToClientInfoException(
1689            SQLException x) {
1690        if (x instanceof SQLClientInfoException) {
1691            return (SQLClientInfoException) x;
1692        }
1693        return new SQLClientInfoException(
1694                x.getMessage(), x.getSQLState(), x.getErrorCode(), null, null);
1695    }
1696 
1697    /**
1698     * Set the client properties.
1699     * This method always throws a SQLClientInfoException.
1700     *
1701     * @param properties the properties (ignored)
1702     */
1703    @Override
1704    public void setClientInfo(Properties properties) throws SQLClientInfoException {
1705        try {
1706            if (isDebugEnabled()) {
1707                debugCode("setClientInfo(properties);");
1708            }
1709            checkClosed();
1710            // we don't have any client properties, so just throw
1711            throw new SQLClientInfoException();
1712        } catch (Exception e) {
1713            throw convertToClientInfoException(logAndConvert(e));
1714        }
1715    }
1716 
1717    /**
1718     * Get the client properties.
1719     *
1720     * @return the property list
1721     */
1722    @Override
1723    public Properties getClientInfo() throws SQLException {
1724        try {
1725            if (isDebugEnabled()) {
1726                debugCode("getClientInfo();");
1727            }
1728            checkClosed();
1729            ArrayList<String> serverList = session.getClusterServers();
1730            Properties p = new Properties();
1731 
1732            p.setProperty("numServers", String.valueOf(serverList.size()));
1733            for (int i = 0; i < serverList.size(); i++) {
1734                p.setProperty("server" + String.valueOf(i), serverList.get(i));
1735            }
1736            return p;
1737        } catch (Exception e) {
1738            throw logAndConvert(e);
1739        }
1740    }
1741 
1742    /**
1743     * Get a client property.
1744     *
1745     * @param name the client info name (ignored)
1746     * @return the property value
1747     */
1748    @Override
1749    public String getClientInfo(String name) throws SQLException {
1750        try {
1751            if (isDebugEnabled()) {
1752                debugCodeCall("getClientInfo", name);
1753            }
1754            checkClosed();
1755            Properties p = getClientInfo();
1756            String s = p.getProperty(name);
1757            if (s == null) {
1758                throw new SQLClientInfoException();
1759            }
1760            return s;
1761        } catch (Exception e) {
1762            throw logAndConvert(e);
1763        }
1764    }
1765 
1766    /**
1767     * Return an object of this class if possible.
1768     *
1769     * @param iface the class
1770     * @return this
1771     */
1772    @Override
1773    @SuppressWarnings("unchecked")
1774    public <T> T unwrap(Class<T> iface) throws SQLException {
1775        if (isWrapperFor(iface)) {
1776            return (T) this;
1777        }
1778        throw DbException.getInvalidValueException("iface", iface);
1779    }
1780 
1781    /**
1782     * Checks if unwrap can return an object of this class.
1783     *
1784     * @param iface the class
1785     * @return whether or not the interface is assignable from this class
1786     */
1787    @Override
1788    public boolean isWrapperFor(Class<?> iface) throws SQLException {
1789        return iface != null && iface.isAssignableFrom(getClass());
1790    }
1791 
1792    /**
1793     * Create a Clob value from this reader.
1794     *
1795     * @param x the reader
1796     * @param length the length (if smaller or equal than 0, all data until the
1797     *            end of file is read)
1798     * @return the value
1799     */
1800    public Value createClob(Reader x, long length) {
1801        if (x == null) {
1802            return ValueNull.INSTANCE;
1803        }
1804        if (length <= 0) {
1805            length = -1;
1806        }
1807        Value v = session.getDataHandler().getLobStorage().createClob(x, length);
1808        session.addTemporaryLob(v);
1809        return v;
1810    }
1811 
1812    /**
1813     * Create a Blob value from this input stream.
1814     *
1815     * @param x the input stream
1816     * @param length the length (if smaller or equal than 0, all data until the
1817     *            end of file is read)
1818     * @return the value
1819     */
1820    public Value createBlob(InputStream x, long length) {
1821        if (x == null) {
1822            return ValueNull.INSTANCE;
1823        }
1824        if (length <= 0) {
1825            length = -1;
1826        }
1827        Value v = session.getDataHandler().getLobStorage().createBlob(x, length);
1828        session.addTemporaryLob(v);
1829        return v;
1830    }
1831 
1832    /**
1833     * [Not supported]
1834     *
1835     * @param schema the schema
1836     */
1837//## Java 1.7 ##
1838    @Override
1839    public void setSchema(String schema) {
1840        // not supported
1841    }
1842//*/
1843 
1844    /**
1845     * [Not supported]
1846     */
1847//## Java 1.7 ##
1848    @Override
1849    public String getSchema() {
1850        return null;
1851    }
1852//*/
1853 
1854    /**
1855     * [Not supported]
1856     *
1857     * @param executor the executor used by this method
1858     */
1859//## Java 1.7 ##
1860    @Override
1861    public void abort(Executor executor) {
1862        // not supported
1863    }
1864//*/
1865 
1866    /**
1867     * [Not supported]
1868     *
1869     * @param executor the executor used by this method
1870     * @param milliseconds the TCP connection timeout
1871     */
1872//## Java 1.7 ##
1873    @Override
1874    public void setNetworkTimeout(Executor executor, int milliseconds) {
1875        // not supported
1876    }
1877//*/
1878 
1879    /**
1880     * [Not supported]
1881     */
1882//## Java 1.7 ##
1883    @Override
1884    public int getNetworkTimeout() {
1885        return 0;
1886    }
1887//*/
1888 
1889    /**
1890     * Check that the given type map is either null or empty.
1891     *
1892     * @param map the type map
1893     * @throws DbException if the map is not empty
1894     */
1895    static void checkMap(Map<String, Class<?>> map) {
1896        if (map != null && map.size() > 0) {
1897            throw DbException.getUnsupportedException("map.size > 0");
1898        }
1899    }
1900 
1901    /**
1902     * INTERNAL
1903     */
1904    @Override
1905    public String toString() {
1906        return getTraceObjectName() + ": url=" + url + " user=" + user;
1907    }
1908 
1909    /**
1910     * Convert an object to the default Java object for the given SQL type. For
1911     * example, LOB objects are converted to java.sql.Clob / java.sql.Blob.
1912     *
1913     * @param v the value
1914     * @return the object
1915     */
1916    Object convertToDefaultObject(Value v) {
1917        Object o;
1918        switch (v.getType()) {
1919        case Value.CLOB: {
1920            int id = getNextId(TraceObject.CLOB);
1921            o = new JdbcClob(this, v, id);
1922            break;
1923        }
1924        case Value.BLOB: {
1925            int id = getNextId(TraceObject.BLOB);
1926            o = new JdbcBlob(this, v, id);
1927            break;
1928        }
1929        case Value.JAVA_OBJECT:
1930            if (SysProperties.serializeJavaObject) {
1931                o = JdbcUtils.deserialize(v.getBytesNoCopy(), session.getDataHandler());
1932                break;
1933            }
1934        default:
1935            o = v.getObject();
1936        }
1937        return o;
1938    }
1939 
1940    CompareMode getCompareMode() {
1941        return compareMode;
1942    }
1943 
1944    /**
1945     * INTERNAL
1946     */
1947    public void setTraceLevel(int level) {
1948        trace.setLevel(level);
1949    }
1950 
1951}

[all classes][org.h2.jdbc]
EMMA 2.0.5312 (C) Vladimir Roubtsov