Unverified 提交 1fc1d43e authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1729 from katzyn/PreparedStatement

Parse parameters properly in prepared statements with multiple commands
......@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #1590: Error on executing "DELETE FROM table1 WHERE ID = ?; DELETE FROM table2 WHERE ID = ?;"
</li>
<li>Issue #1727: Support ISODOW as identifier for the extract function additional to ISO_DAY_OF_WEEK
</li>
<li>PR #1580, #1726: Disable remote database creation by default
......
......@@ -185,4 +185,8 @@ public class CommandContainer extends Command {
return prepared.getType();
}
void clearCTE() {
clearCTE(session, prepared);
}
}
......@@ -8,6 +8,7 @@ package org.h2.command;
import java.util.ArrayList;
import org.h2.engine.Session;
import org.h2.expression.Parameter;
import org.h2.expression.ParameterInterface;
import org.h2.result.ResultInterface;
......@@ -16,28 +17,45 @@ import org.h2.result.ResultInterface;
*/
class CommandList extends Command {
private final Command command;
private final String remaining;
private CommandContainer command;
private final ArrayList<Prepared> commands;
private final ArrayList<Parameter> parameters;
private String remaining;
private Command remainingCommand;
CommandList(Session session, String sql, Command c, String remaining) {
CommandList(Session session, String sql, CommandContainer command, ArrayList<Prepared> commands,
ArrayList<Parameter> parameters, String remaining) {
super(session, sql);
this.command = c;
this.command = command;
this.commands = commands;
this.parameters = parameters;
this.remaining = remaining;
}
@Override
public ArrayList<? extends ParameterInterface> getParameters() {
return command.getParameters();
return parameters;
}
private void executeRemaining() {
Command remainingCommand = session.prepareLocal(remaining);
for (Prepared prepared : commands) {
prepared.prepare();
if (prepared.isQuery()) {
prepared.query(0);
} else {
prepared.update();
}
}
if (remaining != null) {
remainingCommand = session.prepareLocal(remaining);
remaining = null;
if (remainingCommand.isQuery()) {
remainingCommand.query(0);
} else {
remainingCommand.update();
}
}
}
@Override
public int update() {
......@@ -58,6 +76,17 @@ class CommandList extends Command {
return result;
}
@Override
public void stop() {
command.stop();
for (Prepared prepared : commands) {
CommandContainer.clearCTE(session, prepared);
}
if (remainingCommand != null) {
remainingCommand.stop();
}
}
@Override
public boolean isQuery() {
return command.isQuery();
......
......@@ -634,14 +634,15 @@ public class Parser {
private Prepared currentPrepared;
private Select currentSelect;
private ArrayList<Parameter> parameters;
private ArrayList<Parameter> indexedParameterList;
private ArrayList<Parameter> suppliedParameters;
private ArrayList<Parameter> suppliedParameterList;
private String schemaName;
private ArrayList<String> expectedList;
private boolean rightsChecked;
private boolean recompileAlways;
private boolean literalsChecked;
private ArrayList<Parameter> indexedParameterList;
private int orderInFrom;
private ArrayList<Parameter> suppliedParameterList;
public Parser(Session session) {
this.database = session.getDatabase();
......@@ -673,8 +674,8 @@ public class Parser {
public Command prepareCommand(String sql) {
try {
Prepared p = parse(sql);
boolean hasMore = isToken(SEMICOLON);
if (!hasMore && currentTokenType != END) {
if (currentTokenType != SEMICOLON && currentTokenType != END) {
addExpected(SEMICOLON);
throw getSyntaxError();
}
try {
......@@ -683,11 +684,14 @@ public class Parser {
CommandContainer.clearCTE(session, p);
throw t;
}
Command c = new CommandContainer(session, sql, p);
if (hasMore) {
if (parseIndex < sql.length()) {
sql = sql.substring(0, parseIndex);
}
CommandContainer c = new CommandContainer(session, sql, p);
if (currentTokenType == SEMICOLON) {
String remaining = originalSQL.substring(parseIndex);
if (!StringUtils.isWhitespaceOrEmpty(remaining)) {
c = new CommandList(session, sql, c, remaining);
return prepareCommandList(c, sql, remaining);
}
}
return c;
......@@ -696,6 +700,46 @@ public class Parser {
}
}
private CommandList prepareCommandList(CommandContainer command, String sql, String remaining) {
try {
ArrayList<Prepared> list = Utils.newSmallArrayList();
boolean stop = false;
do {
if (stop) {
return new CommandList(session, sql, command, list, parameters, remaining);
}
suppliedParameters = parameters;
suppliedParameterList = indexedParameterList;
Prepared p;
try {
p = parse(remaining);
} catch (DbException ex) {
// This command may depend on results of previous commands.
if (ex.getErrorCode() == ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS) {
throw ex;
}
return new CommandList(session, sql, command, list, parameters, remaining);
}
if (p instanceof DefineCommand) {
// Next commands may depend on results of this command.
stop = true;
}
list.add(p);
if (currentTokenType == END) {
break;
}
if (currentTokenType != SEMICOLON) {
addExpected(SEMICOLON);
throw getSyntaxError();
}
} while (!StringUtils.isWhitespaceOrEmpty(remaining = originalSQL.substring(parseIndex)));
return new CommandList(session, sql, command, list, parameters, null);
} catch (Throwable t) {
command.clearCTE();
throw t;
}
}
/**
* Parse the statement, but don't prepare it for execution.
*
......@@ -727,12 +771,12 @@ public class Parser {
} else {
expectedList = null;
}
parameters = Utils.newSmallArrayList();
parameters = suppliedParameters != null ? suppliedParameters : Utils.<Parameter>newSmallArrayList();
indexedParameterList = suppliedParameterList;
currentSelect = null;
currentPrepared = null;
createView = null;
recompileAlways = false;
indexedParameterList = suppliedParameterList;
read();
return parsePrepared();
}
......@@ -3834,6 +3878,7 @@ public class Parser {
if (p == null) {
p = new Parameter(index);
indexedParameterList.set(index, p);
parameters.add(p);
}
read();
} else {
......@@ -3843,8 +3888,8 @@ public class Parser {
.get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
}
p = new Parameter(parameters.size());
}
parameters.add(p);
}
return p;
}
......
......@@ -199,6 +199,7 @@ public class TestPreparedStatement extends TestDb {
testColumnMetaDataWithEquals(conn);
testColumnMetaDataWithIn(conn);
testValueResultSet(conn);
testMultipleStatements(conn);
conn.close();
testPreparedStatementWithLiteralsNone();
testPreparedStatementWithIndexedParameterAndLiteralsNone();
......@@ -1758,4 +1759,31 @@ public class TestPreparedStatement extends TestDb {
}
}
private void testMultipleStatements(Connection conn) throws SQLException {
assertThrows(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS, conn).prepareStatement("SELECT ?; SELECT ?1");
assertThrows(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS, conn).prepareStatement("SELECT ?1; SELECT ?");
Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE TEST (ID IDENTITY, V INT)");
PreparedStatement ps = conn.prepareStatement("INSERT INTO TEST(V) VALUES ?; INSERT INTO TEST(V) VALUES ?");
ps.setInt(1, 1);
ps.setInt(2, 2);
ps.executeUpdate();
ps = conn.prepareStatement("INSERT INTO TEST(V) VALUES ?2; INSERT INTO TEST(V) VALUES ?1;");
ps.setInt(1, 3);
ps.setInt(2, 4);
ps.executeUpdate();
try (ResultSet rs = stmt.executeQuery("SELECT V FROM TEST ORDER BY ID")) {
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertTrue(rs.next());
assertEquals(2, rs.getInt(1));
assertTrue(rs.next());
assertEquals(4, rs.getInt(1));
assertTrue(rs.next());
assertEquals(3, rs.getInt(1));
assertFalse(rs.next());
}
stmt.execute("DROP TABLE TEST");
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论