Unverified 提交 734f8029 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1671 from katzyn/misc

Assorted changes
...@@ -41,7 +41,8 @@ of H2 please use parentheses. ...@@ -41,7 +41,8 @@ of H2 please use parentheses.
If FOR UPDATE is specified, the tables or rows are locked for writing. If FOR UPDATE is specified, the tables or rows are locked for writing.
This clause is not allowed in DISTINCT queries and in queries with non-window aggregates, GROUP BY, or HAVING clauses. This clause is not allowed in DISTINCT queries and in queries with non-window aggregates, GROUP BY, or HAVING clauses.
When using default MVStore engine only the selected rows are locked as in an UPDATE statement. When using default MVStore engine only the selected rows are locked as in an UPDATE statement;
locking behavior for rows that were excluded from result using OFFSET / FETCH is undefined.
With PageStore engine the whole tables are locked. With PageStore engine the whole tables are locked.
"," ","
SELECT * FROM TEST; SELECT * FROM TEST;
...@@ -5559,7 +5560,7 @@ OVER windowNameOrSpecification ...@@ -5559,7 +5560,7 @@ OVER windowNameOrSpecification
"," ","
Returns the ratio of a value to the sum of all values. Returns the ratio of a value to the sum of all values.
If argument is NULL or sum of all values is 0, then the value of function is NULL. If argument is NULL or sum of all values is 0, then the value of function is NULL.
Window frame clause is not allowed for this function. Window ordering and window frame clauses are not allowed for this function.
Window functions are currently experimental in H2 and should be used with caution. Window functions are currently experimental in H2 and should be used with caution.
They also may require a lot of memory for large queries. They also may require a lot of memory for large queries.
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package org.h2.command; package org.h2.command;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.command.dml.Explain; import org.h2.command.dml.Explain;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
...@@ -27,6 +28,35 @@ public class CommandContainer extends Command { ...@@ -27,6 +28,35 @@ public class CommandContainer extends Command {
private boolean readOnlyKnown; private boolean readOnlyKnown;
private boolean readOnly; private boolean readOnly;
/**
* Clears CTE views for a specified statement.
*
* @param session the session
* @param prepared prepared statement
*/
static void clearCTE(Session session, Prepared prepared) {
List<TableView> cteCleanups = prepared.getCteCleanups();
if (cteCleanups != null) {
clearCTE(session, cteCleanups);
}
}
/**
* Clears CTE views.
*
* @param session the session
* @param views list of view
*/
static void clearCTE(Session session, List<TableView> views) {
for (TableView view : views) {
// check if view was previously deleted as their name is set to
// null
if (view.getName() != null) {
session.removeLocalTempTable(view);
}
}
}
CommandContainer(Session session, String sql, Prepared prepared) { CommandContainer(Session session, String sql, Prepared prepared) {
super(session, sql); super(session, sql);
prepared.setCommand(this); prepared.setCommand(this);
...@@ -123,15 +153,7 @@ public class CommandContainer extends Command { ...@@ -123,15 +153,7 @@ public class CommandContainer extends Command {
super.stop(); super.stop();
// Clean up after the command was run in the session. // Clean up after the command was run in the session.
// Must restart query (and dependency construction) to reuse. // Must restart query (and dependency construction) to reuse.
if (prepared.getCteCleanups() != null) { clearCTE(session, prepared);
for (TableView view : prepared.getCteCleanups()) {
// check if view was previously deleted as their name is set to
// null
if (view.getName() != null) {
session.removeLocalTempTable(view);
}
}
}
} }
@Override @Override
......
...@@ -672,7 +672,12 @@ public class Parser { ...@@ -672,7 +672,12 @@ public class Parser {
if (!hasMore && currentTokenType != END) { if (!hasMore && currentTokenType != END) {
throw getSyntaxError(); throw getSyntaxError();
} }
p.prepare(); try {
p.prepare();
} catch (Throwable t) {
CommandContainer.clearCTE(session, p);
throw t;
}
Command c = new CommandContainer(session, sql, p); Command c = new CommandContainer(session, sql, p);
if (hasMore) { if (hasMore) {
String remaining = originalSQL.substring(parseIndex); String remaining = originalSQL.substring(parseIndex);
...@@ -4533,14 +4538,20 @@ public class Parser { ...@@ -4533,14 +4538,20 @@ public class Parser {
parseIndex = i; parseIndex = i;
return; return;
case CHAR_VALUE: case CHAR_VALUE:
if (c == '0' && chars[i] == 'X') { if (c == '0' && (chars[i] == 'X' || chars[i] == 'x')) {
// hex number // hex number
long number = 0; long number = 0;
start += 2; start += 2;
i++; i++;
while (true) { while (true) {
c = chars[i]; c = chars[i];
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) { if (c >= '0' && c <= '9') {
number = (number << 4) + c - '0';
} else if (c >= 'A' && c <= 'F') {
number = (number << 4) + c - ('A' - 10);
} else if (c >= 'a' && c <= 'f') {
number = (number << 4) + c - ('a' - 10);
} else {
checkLiterals(false); checkLiterals(false);
currentValue = ValueInt.get((int) number); currentValue = ValueInt.get((int) number);
currentTokenType = VALUE; currentTokenType = VALUE;
...@@ -4548,8 +4559,6 @@ public class Parser { ...@@ -4548,8 +4559,6 @@ public class Parser {
parseIndex = i; parseIndex = i;
return; return;
} }
number = (number << 4) + c -
(c >= 'A' ? ('A' - 0xa) : ('0'));
if (number > Integer.MAX_VALUE) { if (number > Integer.MAX_VALUE) {
readHexDecimal(start, i); readHexDecimal(start, i);
return; return;
...@@ -4558,12 +4567,19 @@ public class Parser { ...@@ -4558,12 +4567,19 @@ public class Parser {
} }
} }
long number = c - '0'; long number = c - '0';
while (true) { loop: while (true) {
c = chars[i]; c = chars[i];
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
if (c == '.' || c == 'E' || c == 'L') { switch (c) {
readDecimal(start, i); case '.':
break; case 'E':
case 'e':
readDecimal(start, i, false);
break loop;
case 'L':
case 'l':
readDecimal(start, i, true);
break loop;
} }
checkLiterals(false); checkLiterals(false);
currentValue = ValueInt.get((int) number); currentValue = ValueInt.get((int) number);
...@@ -4574,7 +4590,7 @@ public class Parser { ...@@ -4574,7 +4590,7 @@ public class Parser {
} }
number = number * 10 + (c - '0'); number = number * 10 + (c - '0');
if (number > Integer.MAX_VALUE) { if (number > Integer.MAX_VALUE) {
readDecimal(start, i); readDecimal(start, i, true);
break; break;
} }
i++; i++;
...@@ -4587,7 +4603,7 @@ public class Parser { ...@@ -4587,7 +4603,7 @@ public class Parser {
parseIndex = i; parseIndex = i;
return; return;
} }
readDecimal(i - 1, i); readDecimal(i - 1, i, false);
return; return;
case CHAR_STRING: { case CHAR_STRING: {
String result = null; String result = null;
...@@ -4684,22 +4700,24 @@ public class Parser { ...@@ -4684,22 +4700,24 @@ public class Parser {
currentTokenType = VALUE; currentTokenType = VALUE;
} }
private void readDecimal(int start, int i) { private void readDecimal(int start, int i, boolean integer) {
char[] chars = sqlCommandChars; char[] chars = sqlCommandChars;
int[] types = characterTypes; int[] types = characterTypes;
// go until the first non-number // go until the first non-number
while (true) { while (true) {
int t = types[i]; int t = types[i];
if (t != CHAR_DOT && t != CHAR_VALUE) { if (t == CHAR_DOT) {
integer = false;
} else if (t != CHAR_VALUE) {
break; break;
} }
i++; i++;
} }
boolean containsE = false; char c = chars[i];
if (chars[i] == 'E' || chars[i] == 'e') { if (c == 'E' || c == 'e') {
containsE = true; integer = false;
i++; c = chars[++i];
if (chars[i] == '+' || chars[i] == '-') { if (c == '+' || c == '-') {
i++; i++;
} }
if (types[i] != CHAR_VALUE) { if (types[i] != CHAR_VALUE) {
...@@ -4710,14 +4728,14 @@ public class Parser { ...@@ -4710,14 +4728,14 @@ public class Parser {
} }
} }
parseIndex = i; parseIndex = i;
String sub = sqlCommand.substring(start, i);
checkLiterals(false); checkLiterals(false);
BigDecimal bd; BigDecimal bd;
if (!containsE && sub.indexOf('.') < 0) { if (integer && i - start <= 19) {
BigInteger bi = new BigInteger(sub); BigInteger bi = new BigInteger(sqlCommand.substring(start, i));
if (bi.compareTo(ValueLong.MAX_BI) <= 0) { if (bi.compareTo(ValueLong.MAX_BI) <= 0) {
// parse constants like "10000000L" // parse constants like "10000000L"
if (chars[i] == 'L') { c = chars[i];
if (c == 'L' || c == 'l') {
parseIndex++; parseIndex++;
} }
currentValue = ValueLong.get(bi.longValue()); currentValue = ValueLong.get(bi.longValue());
...@@ -4727,9 +4745,9 @@ public class Parser { ...@@ -4727,9 +4745,9 @@ public class Parser {
bd = new BigDecimal(bi); bd = new BigDecimal(bi);
} else { } else {
try { try {
bd = new BigDecimal(sub); bd = new BigDecimal(sqlCommandChars, start, i - start);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, e, sub); throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, e, sqlCommand.substring(start, i));
} }
} }
currentValue = ValueDecimal.get(bd); currentValue = ValueDecimal.get(bd);
...@@ -6127,6 +6145,15 @@ public class Parser { ...@@ -6127,6 +6145,15 @@ public class Parser {
private Prepared parseWith() { private Prepared parseWith() {
List<TableView> viewsCreated = new ArrayList<>(); List<TableView> viewsCreated = new ArrayList<>();
try {
return parseWith1(viewsCreated);
} catch (Throwable t) {
CommandContainer.clearCTE(session, viewsCreated);
throw t;
}
}
private Prepared parseWith1(List<TableView> viewsCreated) {
readIf("RECURSIVE"); readIf("RECURSIVE");
// This WITH statement is not a temporary view - it is part of a persistent view // This WITH statement is not a temporary view - it is part of a persistent view
......
...@@ -774,7 +774,7 @@ public class Select extends Query { ...@@ -774,7 +774,7 @@ public class Select extends Query {
} }
boolean fetchPercent = this.fetchPercent; boolean fetchPercent = this.fetchPercent;
if (fetchPercent) { if (fetchPercent) {
// Need to check it row, because negative limit has special treatment later // Need to check it now, because negative limit has special treatment later
if (limitRows < 0 || limitRows > 100) { if (limitRows < 0 || limitRows > 100) {
throw DbException.getInvalidValueException("FETCH PERCENT", limitRows); throw DbException.getInvalidValueException("FETCH PERCENT", limitRows);
} }
...@@ -803,7 +803,7 @@ public class Select extends Query { ...@@ -803,7 +803,7 @@ public class Select extends Query {
} }
// Do not add rows before OFFSET to result if possible // Do not add rows before OFFSET to result if possible
boolean quickOffset = !fetchPercent; boolean quickOffset = !fetchPercent;
if (sort != null && (!sortUsingIndex || isAnyDistinct() || withTies)) { if (sort != null && (!sortUsingIndex || isAnyDistinct())) {
result = createLocalResult(result); result = createLocalResult(result);
result.setSortOrder(sort); result.setSortOrder(sort);
if (!sortUsingIndex) { if (!sortUsingIndex) {
...@@ -886,7 +886,9 @@ public class Select extends Query { ...@@ -886,7 +886,9 @@ public class Select extends Query {
if (limitRows >= 0) { if (limitRows >= 0) {
result.setLimit(limitRows); result.setLimit(limitRows);
result.setFetchPercent(fetchPercent); result.setFetchPercent(fetchPercent);
result.setWithTies(withTies); if (withTies) {
result.setWithTies(sort);
}
} }
if (result != null) { if (result != null) {
result.done(); result.done();
......
...@@ -264,7 +264,9 @@ public class SelectUnion extends Query { ...@@ -264,7 +264,9 @@ public class SelectUnion extends Query {
if (v != ValueNull.INSTANCE) { if (v != ValueNull.INSTANCE) {
result.setLimit(v.getInt()); result.setLimit(v.getInt());
result.setFetchPercent(fetchPercent); result.setFetchPercent(fetchPercent);
result.setWithTies(withTies); if (withTies) {
result.setWithTies(sort);
}
} }
} }
l.close(); l.close();
......
...@@ -451,6 +451,9 @@ public class WindowFunction extends DataAnalysisOperation { ...@@ -451,6 +451,9 @@ public class WindowFunction extends DataAnalysisOperation {
throw DbException.getSyntaxError(sql, sql.length() - 1, "ORDER BY"); throw DbException.getSyntaxError(sql, sql.length() - 1, "ORDER BY");
default: default:
} }
} else if (type == WindowFunctionType.RATIO_TO_REPORT) {
String sql = getSQL();
throw DbException.getSyntaxError(sql, sql.length() - 1);
} }
super.optimize(session); super.optimize(session);
if (args != null) { if (args != null) {
......
...@@ -25,7 +25,6 @@ import org.h2.message.DbException; ...@@ -25,7 +25,6 @@ import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
import org.h2.result.SimpleResult; import org.h2.result.SimpleResult;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
import org.h2.value.ValueString; import org.h2.value.ValueString;
...@@ -1616,25 +1615,27 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -1616,25 +1615,27 @@ public class JdbcDatabaseMetaData extends TraceObject implements
+ "FROM INFORMATION_SCHEMA.HELP WHERE SECTION = ?"); + "FROM INFORMATION_SCHEMA.HELP WHERE SECTION = ?");
prep.setString(1, section); prep.setString(1, section);
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
StatementBuilder buff = new StatementBuilder(); StringBuilder builder = new StringBuilder();
while (rs.next()) { while (rs.next()) {
String s = rs.getString(1).trim(); String s = rs.getString(1).trim();
String[] array = StringUtils.arraySplit(s, ',', true); String[] array = StringUtils.arraySplit(s, ',', true);
for (String a : array) { for (String a : array) {
buff.appendExceptFirst(","); if (builder.length() != 0) {
builder.append(',');
}
String f = a.trim(); String f = a.trim();
int spaceIndex = f.indexOf(' '); int spaceIndex = f.indexOf(' ');
if (spaceIndex >= 0) { if (spaceIndex >= 0) {
// remove 'Function' from 'INSERT Function' // remove 'Function' from 'INSERT Function'
StringUtils.trimSubstring(buff.builder(), f, 0, spaceIndex); StringUtils.trimSubstring(builder, f, 0, spaceIndex);
} else { } else {
buff.append(f); builder.append(f);
} }
} }
} }
rs.close(); rs.close();
prep.close(); prep.close();
return buff.toString(); return builder.toString();
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
} }
......
...@@ -10,9 +10,7 @@ import java.util.ArrayList; ...@@ -10,9 +10,7 @@ import java.util.ArrayList;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.expression.ParameterInterface; import org.h2.expression.ParameterInterface;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.Value;
/** /**
* This class represents a trace module. * This class represents a trace module.
...@@ -239,29 +237,23 @@ public class Trace { ...@@ -239,29 +237,23 @@ public class Trace {
* @param parameters the parameter list * @param parameters the parameter list
* @return the formatted text * @return the formatted text
*/ */
public static String formatParams( public static String formatParams(ArrayList<? extends ParameterInterface> parameters) {
ArrayList<? extends ParameterInterface> parameters) {
if (parameters.isEmpty()) { if (parameters.isEmpty()) {
return ""; return "";
} }
StatementBuilder buff = new StatementBuilder(); StringBuilder builder = new StringBuilder();
int i = 0; int i = 0;
boolean params = false;
for (ParameterInterface p : parameters) { for (ParameterInterface p : parameters) {
if (p.isValueSet()) { if (p.isValueSet()) {
if (!params) { builder.append(i == 0 ? " {" : ", ") //
buff.append(" {"); .append(++i).append(": ") //
params = true; .append(p.getParamValue().getTraceSQL());
}
buff.appendExceptFirst(", ");
Value v = p.getParamValue();
buff.append(++i).append(": ").append(v.getTraceSQL());
} }
} }
if (params) { if (i != 0) {
buff.append('}'); builder.append('}');
} }
return buff.toString(); return builder.toString();
} }
/** /**
......
...@@ -25,7 +25,10 @@ public interface LocalResult extends ResultInterface, ResultTarget { ...@@ -25,7 +25,10 @@ public interface LocalResult extends ResultInterface, ResultTarget {
public void setMaxMemoryRows(int maxValue); public void setMaxMemoryRows(int maxValue);
/** /**
* @param sort Sort order. * Sets sort order to be used by this result. When rows are presorted by the
* query this method should not be used.
*
* @param sort the sort order
*/ */
public void setSortOrder(SortOrder sort); public void setSortOrder(SortOrder sort);
...@@ -82,9 +85,14 @@ public interface LocalResult extends ResultInterface, ResultTarget { ...@@ -82,9 +85,14 @@ public interface LocalResult extends ResultInterface, ResultTarget {
public void setFetchPercent(boolean fetchPercent); public void setFetchPercent(boolean fetchPercent);
/** /**
* @param withTies whether tied rows should be included in result too * Enables inclusion of tied rows to result and sets the sort order for tied
* rows. The specified sort order must be the same as sort order if sort
* order was set. Passed value will be used if sort order was not set that
* is possible when rows are presorted.
*
* @param withTiesSortOrder the sort order for tied rows
*/ */
public void setWithTies(boolean withTies); public void setWithTies(SortOrder withTiesSortOrder);
/** /**
* Set the offset of the first row to return. * Set the offset of the first row to return.
......
...@@ -38,7 +38,7 @@ public class LocalResultImpl implements LocalResult { ...@@ -38,7 +38,7 @@ public class LocalResultImpl implements LocalResult {
private int offset; private int offset;
private int limit = -1; private int limit = -1;
private boolean fetchPercent; private boolean fetchPercent;
private boolean withTies; private SortOrder withTiesSortOrder;
private boolean limitsWereApplied; private boolean limitsWereApplied;
private ResultExternal external; private ResultExternal external;
private boolean distinct; private boolean distinct;
...@@ -132,11 +132,6 @@ public class LocalResultImpl implements LocalResult { ...@@ -132,11 +132,6 @@ public class LocalResultImpl implements LocalResult {
return copy; return copy;
} }
/**
* Set the sort order.
*
* @param sort the sort order
*/
@Override @Override
public void setSortOrder(SortOrder sort) { public void setSortOrder(SortOrder sort) {
this.sort = sort; this.sort = sort;
...@@ -365,8 +360,8 @@ public class LocalResultImpl implements LocalResult { ...@@ -365,8 +360,8 @@ public class LocalResultImpl implements LocalResult {
if (isAnyDistinct()) { if (isAnyDistinct()) {
rows = distinctRows.values(); rows = distinctRows.values();
} }
if (sort != null && limit != 0) { if (sort != null && limit != 0 && !limitsWereApplied) {
boolean withLimit = limit > 0 && !withTies; boolean withLimit = limit > 0 && withTiesSortOrder == null;
if (offset > 0 || withLimit) { if (offset > 0 || withLimit) {
sort.sort(rows, offset, withLimit ? limit : rows.size()); sort.sort(rows, offset, withLimit ? limit : rows.size());
} else { } else {
...@@ -412,9 +407,9 @@ public class LocalResultImpl implements LocalResult { ...@@ -412,9 +407,9 @@ public class LocalResultImpl implements LocalResult {
return; return;
} }
int to = offset + limit; int to = offset + limit;
if (withTies && sort != null) { if (withTiesSortOrder != null) {
Value[] expected = rows.get(to - 1); Value[] expected = rows.get(to - 1);
while (to < rows.size() && sort.compare(expected, rows.get(to)) == 0) { while (to < rows.size() && withTiesSortOrder.compare(expected, rows.get(to)) == 0) {
to++; to++;
rowCount++; rowCount++;
} }
...@@ -448,9 +443,9 @@ public class LocalResultImpl implements LocalResult { ...@@ -448,9 +443,9 @@ public class LocalResultImpl implements LocalResult {
addRowsToDisk(); addRowsToDisk();
} }
} }
if (withTies && sort != null && row != null) { if (withTiesSortOrder != null && row != null) {
Value[] expected = row; Value[] expected = row;
while ((row = temp.next()) != null && sort.compare(expected, row) == 0) { while ((row = temp.next()) != null && withTiesSortOrder.compare(expected, row) == 0) {
rows.add(row); rows.add(row);
rowCount++; rowCount++;
if (rows.size() > maxMemoryRows) { if (rows.size() > maxMemoryRows) {
...@@ -497,12 +492,10 @@ public class LocalResultImpl implements LocalResult { ...@@ -497,12 +492,10 @@ public class LocalResultImpl implements LocalResult {
this.fetchPercent = fetchPercent; this.fetchPercent = fetchPercent;
} }
/**
* @param withTies whether tied rows should be included in result too
*/
@Override @Override
public void setWithTies(boolean withTies) { public void setWithTies(SortOrder withTiesSortOrder) {
this.withTies = withTies; assert sort == null || sort == withTiesSortOrder;
this.withTiesSortOrder = withTiesSortOrder;
} }
@Override @Override
......
...@@ -27,9 +27,9 @@ public interface ResultTarget { ...@@ -27,9 +27,9 @@ public interface ResultTarget {
int getRowCount(); int getRowCount();
/** /**
* A hint that offset and limit may be ignored by this result because they * A hint that sorting, offset and limit may be ignored by this result
* were applied during the query. This is useful for WITH TIES clause * because they were applied during the query. This is useful for WITH TIES
* because result may contain tied rows above limit. * clause because result may contain tied rows above limit.
*/ */
void limitsWereApplied(); void limitsWereApplied();
......
...@@ -14,7 +14,6 @@ import java.util.ArrayList; ...@@ -14,7 +14,6 @@ import java.util.ArrayList;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Utils; import org.h2.util.Utils;
import org.h2.value.DataType; import org.h2.value.DataType;
...@@ -156,24 +155,26 @@ public class UpdatableRow { ...@@ -156,24 +155,26 @@ public class UpdatableRow {
return index; return index;
} }
private void appendColumnList(StatementBuilder buff, boolean set) { private void appendColumnList(StringBuilder builder, boolean set) {
buff.resetCount();
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
buff.appendExceptFirst(","); if (i > 0) {
builder.append(',');
}
String col = result.getColumnName(i); String col = result.getColumnName(i);
StringUtils.quoteIdentifier(buff.builder(), col); StringUtils.quoteIdentifier(builder, col);
if (set) { if (set) {
buff.append("=? "); builder.append("=? ");
} }
} }
} }
private void appendKeyCondition(StatementBuilder buff) { private void appendKeyCondition(StringBuilder builder) {
buff.append(" WHERE "); builder.append(" WHERE ");
buff.resetCount(); for (int i = 0; i < key.size(); i++) {
for (String k : key) { if (i > 0) {
buff.appendExceptFirst(" AND "); builder.append(" AND ");
StringUtils.quoteIdentifier(buff.builder(), k).append("=?"); }
StringUtils.quoteIdentifier(builder, key.get(i)).append("=?");
} }
} }
...@@ -218,12 +219,12 @@ public class UpdatableRow { ...@@ -218,12 +219,12 @@ public class UpdatableRow {
* @return the row * @return the row
*/ */
public Value[] readRow(Value[] row) throws SQLException { public Value[] readRow(Value[] row) throws SQLException {
StatementBuilder buff = new StatementBuilder("SELECT "); StringBuilder builder = new StringBuilder("SELECT ");
appendColumnList(buff, false); appendColumnList(builder, false);
buff.append(" FROM "); builder.append(" FROM ");
appendTableName(buff.builder()); appendTableName(builder);
appendKeyCondition(buff); appendKeyCondition(builder);
PreparedStatement prep = conn.prepareStatement(buff.toString()); PreparedStatement prep = conn.prepareStatement(builder.toString());
setKey(prep, 1, row); setKey(prep, 1, row);
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
if (!rs.next()) { if (!rs.next()) {
...@@ -244,10 +245,10 @@ public class UpdatableRow { ...@@ -244,10 +245,10 @@ public class UpdatableRow {
* @throws SQLException if this row has already been deleted * @throws SQLException if this row has already been deleted
*/ */
public void deleteRow(Value[] current) throws SQLException { public void deleteRow(Value[] current) throws SQLException {
StatementBuilder buff = new StatementBuilder("DELETE FROM "); StringBuilder builder = new StringBuilder("DELETE FROM ");
appendTableName(buff.builder()); appendTableName(builder);
appendKeyCondition(buff); appendKeyCondition(builder);
PreparedStatement prep = conn.prepareStatement(buff.toString()); PreparedStatement prep = conn.prepareStatement(builder.toString());
setKey(prep, 1, current); setKey(prep, 1, current);
int count = prep.executeUpdate(); int count = prep.executeUpdate();
if (count != 1) { if (count != 1) {
...@@ -264,15 +265,15 @@ public class UpdatableRow { ...@@ -264,15 +265,15 @@ public class UpdatableRow {
* @throws SQLException if the row has been deleted * @throws SQLException if the row has been deleted
*/ */
public void updateRow(Value[] current, Value[] updateRow) throws SQLException { public void updateRow(Value[] current, Value[] updateRow) throws SQLException {
StatementBuilder buff = new StatementBuilder("UPDATE "); StringBuilder builder = new StringBuilder("UPDATE ");
appendTableName(buff.builder()); appendTableName(builder);
buff.append(" SET "); builder.append(" SET ");
appendColumnList(buff, true); appendColumnList(builder, true);
// TODO updatable result set: we could add all current values to the // TODO updatable result set: we could add all current values to the
// where clause // where clause
// - like this optimistic ('no') locking is possible // - like this optimistic ('no') locking is possible
appendKeyCondition(buff); appendKeyCondition(builder);
PreparedStatement prep = conn.prepareStatement(buff.toString()); PreparedStatement prep = conn.prepareStatement(builder.toString());
int j = 1; int j = 1;
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
Value v = updateRow[i]; Value v = updateRow[i];
...@@ -296,23 +297,24 @@ public class UpdatableRow { ...@@ -296,23 +297,24 @@ public class UpdatableRow {
* @throws SQLException if the row could not be inserted * @throws SQLException if the row could not be inserted
*/ */
public void insertRow(Value[] row) throws SQLException { public void insertRow(Value[] row) throws SQLException {
StatementBuilder buff = new StatementBuilder("INSERT INTO "); StringBuilder builder = new StringBuilder("INSERT INTO ");
appendTableName(buff.builder()); appendTableName(builder);
buff.append('('); builder.append('(');
appendColumnList(buff, false); appendColumnList(builder, false);
buff.append(")VALUES("); builder.append(")VALUES(");
buff.resetCount();
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
buff.appendExceptFirst(","); if (i > 0) {
builder.append(',');
}
Value v = row[i]; Value v = row[i];
if (v == null) { if (v == null) {
buff.append("DEFAULT"); builder.append("DEFAULT");
} else { } else {
buff.append('?'); builder.append('?');
} }
} }
buff.append(')'); builder.append(')');
PreparedStatement prep = conn.prepareStatement(buff.toString()); PreparedStatement prep = conn.prepareStatement(builder.toString());
for (int i = 0, j = 0; i < columnCount; i++) { for (int i = 0, j = 0; i < columnCount; i++) {
Value v = row[i]; Value v = row[i];
if (v != null) { if (v != null) {
......
...@@ -370,10 +370,12 @@ public class StringUtils { ...@@ -370,10 +370,12 @@ public class StringUtils {
if (array == null) { if (array == null) {
return "null"; return "null";
} }
StatementBuilder buff = new StatementBuilder("new String[]{"); StringBuilder buff = new StringBuilder("new String[]{");
for (String a : array) { for (int i = 0; i < array.length; i++) {
buff.appendExceptFirst(", "); if (i > 0) {
buff.append(quoteJavaString(a)); buff.append(", ");
}
buff.append(quoteJavaString(array[i]));
} }
return buff.append('}').toString(); return buff.append('}').toString();
} }
...@@ -389,12 +391,14 @@ public class StringUtils { ...@@ -389,12 +391,14 @@ public class StringUtils {
if (array == null) { if (array == null) {
return "null"; return "null";
} }
StatementBuilder buff = new StatementBuilder("new int[]{"); StringBuilder builder = new StringBuilder("new int[]{");
for (int a : array) { for (int i = 0; i < array.length; i++) {
buff.appendExceptFirst(", "); if (i > 0) {
buff.append(a); builder.append(", ");
}
builder.append(array[i]);
} }
return buff.append('}').toString(); return builder.append('}').toString();
} }
/** /**
...@@ -498,21 +502,24 @@ public class StringUtils { ...@@ -498,21 +502,24 @@ public class StringUtils {
* @return the combined string * @return the combined string
*/ */
public static String arrayCombine(String[] list, char separatorChar) { public static String arrayCombine(String[] list, char separatorChar) {
StatementBuilder buff = new StatementBuilder(); StringBuilder builder = new StringBuilder();
for (String s : list) { for (int i = 0; i < list.length; i++) {
buff.appendExceptFirst(String.valueOf(separatorChar)); if (i > 0) {
builder.append(separatorChar);
}
String s = list[i];
if (s == null) { if (s == null) {
s = ""; continue;
} }
for (int j = 0, length = s.length(); j < length; j++) { for (int j = 0, length = s.length(); j < length; j++) {
char c = s.charAt(j); char c = s.charAt(j);
if (c == '\\' || c == separatorChar) { if (c == '\\' || c == separatorChar) {
buff.append('\\'); builder.append('\\');
} }
buff.append(c); builder.append(c);
} }
} }
return buff.toString(); return builder.toString();
} }
/** /**
......
...@@ -110,6 +110,13 @@ public class TestCompatibility extends TestDb { ...@@ -110,6 +110,13 @@ public class TestCompatibility extends TestDb {
stat.execute("select id from test t group by T.ID"); stat.execute("select id from test t group by T.ID");
stat.execute("drop table test"); stat.execute("drop table test");
rs = stat.executeQuery("select 1e10, 1000000000000000000000e10, 0xfAfBl");
assertTrue(rs.next());
assertEquals(1e10, rs.getDouble(1));
assertEquals(1000000000000000000000e10, rs.getDouble(2));
assertEquals(0xfafbL, rs.getLong(3));
assertFalse(rs.next());
c.close(); c.close();
} }
......
...@@ -146,5 +146,11 @@ WITH CTE_TEST AS (TABLE TEST) ((TABLE CTE_TEST)); ...@@ -146,5 +146,11 @@ WITH CTE_TEST AS (TABLE TEST) ((TABLE CTE_TEST));
> 1 2 > 1 2
> rows: 1 > rows: 1
WITH CTE_TEST AS (TABLE TEST) ((SELECT A, B FROM CTE_TEST2));
> exception TABLE_OR_VIEW_NOT_FOUND_1
WITH CTE_TEST AS (TABLE TEST) ((SELECT A, B, C FROM CTE_TEST));
> exception COLUMN_NOT_FOUND_1
DROP TABLE TEST; DROP TABLE TEST;
> ok > ok
...@@ -31,5 +31,8 @@ SELECT ID, N, RATIO_TO_REPORT(N) OVER() R2R FROM TEST; ...@@ -31,5 +31,8 @@ SELECT ID, N, RATIO_TO_REPORT(N) OVER() R2R FROM TEST;
> 5 -8 null > 5 -8 null
> rows: 5 > rows: 5
SELECT RATIO_TO_REPORT(N) OVER (ORDER BY N) FROM TEST;
> exception SYNTAX_ERROR_1
DROP TABLE TEST; DROP TABLE TEST;
> ok > ok
...@@ -806,3 +806,4 @@ econd irst bcef ordinality nord unnest ...@@ -806,3 +806,4 @@ econd irst bcef ordinality nord unnest
analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan
corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes
preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr
presorted inclusion
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论