📄 jdbcconnection.java
字号:
* <!-- end generic documentation -->
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Up to and including 1.7.2, HSQLDB converts the JDBC SQL
* grammar into the system's native SQL grammar prior to sending
* it, if escape processing is set true; this method returns the
* native form of the statement that the driver would send in place
* of client-specified JDBC SQL grammar. <p>
*
* Before 1.7.2, escape processing was incomplete and
* also broken in terms of support for nested escapes. <p>
*
* Starting with 1.7.2, escape processing is complete and handles nesting
* to arbitrary depth, but enforces a very strict interpretation of the
* syntax and does not detect or process SQL comments. <p>
*
* In essence, the HSQLDB engine directly handles the prescribed syntax
* and date / time formats specified internal to the JDBC escapes.
* It also directly offers the XOpen / ODBC extended scalar
* functions specified available internal to the {fn ...} JDBC escape.
* As such, the driver simply removes the curly braces and JDBC escape
* codes in the simplest and fastest fashion possible, by replacing them
* with whitespace.
*
* But to avoid a great deal of complexity, certain forms of input
* whitespace are currently not recognised. For instance,
* the driver handles "{?= call ...}" but not "{ ?= call ...} or
* "{? = call ...}" <p>
*
* Also, comments embedded in SQL are currently not detected or
* processed and thus may have unexpected effects on the output
* of this method, for instance causing otherwise valid SQL to become
* invalid. It is especially important to be aware of this because escape
* processing is set true by default for Statement objects and is always
* set true when producing a PreparedStatement from prepareStatement()
* or CallableStatement from prepareCall(). Currently, it is simply
* recommended to avoid submitting SQL having comments containing JDBC
* escape sequence patterns and/or single or double quotation marks,
* as this will avoid any potential problems.
*
* It is intended to implement a less strict handling of whitespace and
* proper processing of SQL comments at some point in the near future,
* perhaps before the final 1.7.2 release.
*
* In any event, 1.7.2 now correctly processes the following JDBC escape
* forms to arbitrary nesting depth, but only if the exact whitespace
* layout described below is used: <p>
*
* <ol>
* <li>{call ...}
* <li>{?= call ...}
* <li>{fn ...}
* <li>{oj ...}
* <li>{d ...}
* <li>{t ...}
* <li>{ts ...}
* </ol> <p>
*
* </div> <!-- end release-specific documentation -->
*
* @param sql a SQL statement that may contain one or more '?'
* parameter placeholders
* @return the native form of this statement
* @throws SQLException if a database access error occurs <p>
*/
public synchronized String nativeSQL(final String sql)
throws SQLException {
// boucherb@users 20030405
// FIXME: does not work properly for nested escapes
// e.g. {call ...(...,{ts '...'},....)} does not work
// boucherb@users 20030817
// TESTME: First kick at the FIXME cat done. Now lots of testing
// and refinment
checkClosed();
// CHECKME: Thow or return null if input is null?
if (sql == null || sql.length() == 0 || sql.indexOf('{') == -1) {
return sql;
}
// boolean changed = false;
int state = 0;
int len = sql.length();
int nest = 0;
StringBuffer sb = new StringBuffer(sql.length()); //avoid 16 extra
String msg;
//--
final int outside_all = 0;
final int outside_escape_inside_single_quotes = 1;
final int outside_escape_inside_double_quotes = 2;
//--
final int inside_escape = 3;
final int inside_escape_inside_single_quotes = 4;
final int inside_escape_inside_double_quotes = 5;
// TODO:
// final int inside_single_line_comment = 6;
// final int inside_multi_line_comment = 7;
// Better than old way for large inputs and for avoiding GC overhead;
// toString() reuses internal char[], reducing memory requirment
// and garbage items 3:2
sb.append(sql);
for (int i = 0; i < len; i++) {
char c = sb.charAt(i);
switch (state) {
case outside_all : // Not inside an escape or quotes
if (c == '\'') {
state = outside_escape_inside_single_quotes;
} else if (c == '"') {
state = outside_escape_inside_double_quotes;
} else if (c == '{') {
sb.setCharAt(i++, ' ');
i = StringUtil.skipSpaces(sql, i);
if (sql.regionMatches(true, i, "fn ", 0, 3)
|| sql.regionMatches(true, i, "oj ", 0, 3)
|| sql.regionMatches(true, i, "ts ", 0, 3)) {
sb.setCharAt(i++, ' ');
sb.setCharAt(i++, ' ');
} else if (sql.regionMatches(true, i, "d ", 0, 2)
|| sql.regionMatches(true, i, "t ", 0,
2)) {
sb.setCharAt(i++, ' ');
} else if (sql.regionMatches(true, i, "call ", 0,
5)) {
i += 4;
} else if (sql.regionMatches(true, i, "?= call ", 0,
8)) {
sb.setCharAt(i++, ' ');
sb.setCharAt(i++, ' ');
i += 5;
} else if (sql.regionMatches(true, i, "escape ", 0,
7)) {
i += 6;
} else {
i--;
throw new SQLException(Trace
.getMessage(Trace
.jdbcConnection_nativeSQL, true, new Object[]{ sql
.substring(i) }), "S0010", Trace
.INVALID_JDBC_ARGUMENT);
}
// changed = true;
nest++;
state = inside_escape;
}
break;
case outside_escape_inside_single_quotes : // inside ' ' only
case inside_escape_inside_single_quotes : // inside { } and ' '
if (c == '\'') {
state -= 1;
}
break;
case outside_escape_inside_double_quotes : // inside " " only
case inside_escape_inside_double_quotes : // inside { } and " "
if (c == '"') {
state -= 2;
}
break;
case inside_escape : // inside { }
if (c == '\'') {
state = inside_escape_inside_single_quotes;
} else if (c == '"') {
state = inside_escape_inside_double_quotes;
} else if (c == '}') {
sb.setCharAt(i, ' ');
// changed = true;
nest--;
state = (nest == 0) ? outside_all
: inside_escape;
} else if (c == '{') {
sb.setCharAt(i++, ' ');
if (sql.regionMatches(true, i, "fn ", 0, 3)
|| sql.regionMatches(true, i, "oj ", 0, 3)
|| sql.regionMatches(true, i, "ts ", 0, 3)) {
sb.setCharAt(i++, ' ');
sb.setCharAt(i++, ' ');
} else if (sql.regionMatches(true, i, "d ", 0, 2)
|| sql.regionMatches(true, i, "t ", 0,
2)) {
sb.setCharAt(i++, ' ');
} else if (sql.regionMatches(true, i, "call ", 0,
5)) {
i += 4;
} else if (sql.regionMatches(true, i, "?= call ", 0,
8)) {
sb.setCharAt(i++, ' ');
sb.setCharAt(i++, ' ');
i += 5;
} else if (sql.regionMatches(true, i, "escape ", 0,
7)) {
i += 6;
} else {
i--;
throw new SQLException(Trace
.getMessage(Trace
.jdbcConnection_nativeSQL, true, new Object[]{ sql
.substring(i) }), "S0010", Trace
.INVALID_JDBC_ARGUMENT);
}
// changed = true;
nest++;
state = inside_escape;
}
}
}
return sb.toString();
}
/**
* <!-- start generic documentation -->
* Sets this connection's auto-commit mode to the given state.
* If a connection is in auto-commit mode, then all its SQL
* statements will be executed and committed as individual transactions.
* Otherwise, its SQL statements are grouped into transactions that
* are terminated by a call to either the method <code>commit</code> or
* the method <code>rollback</code>. By default, new connections are
* in auto-commit mode. <p>
*
* The commit occurs when the statement completes or the next
* execute occurs, whichever comes first. In the case of
* statements returning a <code>ResultSet</code> object, the
* statement completes when the last row of the <code>ResultSet</code>
* object has been retrieved or the <code>ResultSet</code> object
* has been closed. In advanced cases, a single statement may
* return multiple results as well as output parameter values. In
* these cases, the commit occurs when all results and output
* parameter values have been retrieved. <p>
*
* <B>NOTE:</B> If this method is called during a transaction,
* the transaction is committed. <p>
*
* <!-- end generic documentation -->
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Up to and including HSQLDB 1.7.2, <p>
*
* <ol>
* <li> All rows of a result set are retrieved internally <em>
* before</em> the first row can actually be fetched.<br>
* Therefore, a statement can be considered complete as soon as
* any XXXStatement.executeXXX method returns. </li>
*
* <li> Multiple result sets and output parameters are not yet
* supported. </li>
* </ol>
* <p>
*
* (boucherb@users) </div> <!-- end release-specific
* documentation -->
*
* @param autoCommit <code>true</code> to enable auto-commit
* mode; <code>false</code> to disable it
* @exception SQLException if a database access error occurs
* @see #getAutoCommit
*/
public synchronized void setAutoCommit(boolean autoCommit)
throws SQLException {
checkClosed();
try {
sessionProxy.setAutoCommit(autoCommit);
} catch (HsqlException e) {
throw Util.sqlException(e);
}
}
/**
* Gets the current auto-commit state.
*
* @return the current state of auto-commit mode
* @exception SQLException Description of the Exception
* @see #setAutoCommit
*/
public synchronized boolean getAutoCommit() throws SQLException {
checkClosed();
try {
return sessionProxy.isAutoCommit();
} catch (HsqlException e) {
throw Util.sqlException(e);
}
}
/**
* <!-- start generic documentation -->
* Makes all changes made since the
* previous commit/rollback permanent and releases any database
* locks currently held by the Connection. This method should be
* used only when auto-commit mode has been disabled. <p>
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -