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 ...@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <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>Issue #1727: Support ISODOW as identifier for the extract function additional to ISO_DAY_OF_WEEK
</li> </li>
<li>PR #1580, #1726: Disable remote database creation by default <li>PR #1580, #1726: Disable remote database creation by default
......
...@@ -185,4 +185,8 @@ public class CommandContainer extends Command { ...@@ -185,4 +185,8 @@ public class CommandContainer extends Command {
return prepared.getType(); return prepared.getType();
} }
void clearCTE() {
clearCTE(session, prepared);
}
} }
...@@ -8,6 +8,7 @@ package org.h2.command; ...@@ -8,6 +8,7 @@ package org.h2.command;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Parameter;
import org.h2.expression.ParameterInterface; import org.h2.expression.ParameterInterface;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
...@@ -16,28 +17,45 @@ import org.h2.result.ResultInterface; ...@@ -16,28 +17,45 @@ import org.h2.result.ResultInterface;
*/ */
class CommandList extends Command { class CommandList extends Command {
private final Command command; private CommandContainer command;
private final String remaining; 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); super(session, sql);
this.command = c; this.command = command;
this.commands = commands;
this.parameters = parameters;
this.remaining = remaining; this.remaining = remaining;
} }
@Override @Override
public ArrayList<? extends ParameterInterface> getParameters() { public ArrayList<? extends ParameterInterface> getParameters() {
return command.getParameters(); return parameters;
} }
private void executeRemaining() { 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()) { if (remainingCommand.isQuery()) {
remainingCommand.query(0); remainingCommand.query(0);
} else { } else {
remainingCommand.update(); remainingCommand.update();
} }
} }
}
@Override @Override
public int update() { public int update() {
...@@ -58,6 +76,17 @@ class CommandList extends Command { ...@@ -58,6 +76,17 @@ class CommandList extends Command {
return result; return result;
} }
@Override
public void stop() {
command.stop();
for (Prepared prepared : commands) {
CommandContainer.clearCTE(session, prepared);
}
if (remainingCommand != null) {
remainingCommand.stop();
}
}
@Override @Override
public boolean isQuery() { public boolean isQuery() {
return command.isQuery(); return command.isQuery();
......
...@@ -634,14 +634,15 @@ public class Parser { ...@@ -634,14 +634,15 @@ public class Parser {
private Prepared currentPrepared; private Prepared currentPrepared;
private Select currentSelect; private Select currentSelect;
private ArrayList<Parameter> parameters; private ArrayList<Parameter> parameters;
private ArrayList<Parameter> indexedParameterList;
private ArrayList<Parameter> suppliedParameters;
private ArrayList<Parameter> suppliedParameterList;
private String schemaName; private String schemaName;
private ArrayList<String> expectedList; private ArrayList<String> expectedList;
private boolean rightsChecked; private boolean rightsChecked;
private boolean recompileAlways; private boolean recompileAlways;
private boolean literalsChecked; private boolean literalsChecked;
private ArrayList<Parameter> indexedParameterList;
private int orderInFrom; private int orderInFrom;
private ArrayList<Parameter> suppliedParameterList;
public Parser(Session session) { public Parser(Session session) {
this.database = session.getDatabase(); this.database = session.getDatabase();
...@@ -673,8 +674,8 @@ public class Parser { ...@@ -673,8 +674,8 @@ public class Parser {
public Command prepareCommand(String sql) { public Command prepareCommand(String sql) {
try { try {
Prepared p = parse(sql); Prepared p = parse(sql);
boolean hasMore = isToken(SEMICOLON); if (currentTokenType != SEMICOLON && currentTokenType != END) {
if (!hasMore && currentTokenType != END) { addExpected(SEMICOLON);
throw getSyntaxError(); throw getSyntaxError();
} }
try { try {
...@@ -683,11 +684,14 @@ public class Parser { ...@@ -683,11 +684,14 @@ public class Parser {
CommandContainer.clearCTE(session, p); CommandContainer.clearCTE(session, p);
throw t; throw t;
} }
Command c = new CommandContainer(session, sql, p); if (parseIndex < sql.length()) {
if (hasMore) { sql = sql.substring(0, parseIndex);
}
CommandContainer c = new CommandContainer(session, sql, p);
if (currentTokenType == SEMICOLON) {
String remaining = originalSQL.substring(parseIndex); String remaining = originalSQL.substring(parseIndex);
if (!StringUtils.isWhitespaceOrEmpty(remaining)) { if (!StringUtils.isWhitespaceOrEmpty(remaining)) {
c = new CommandList(session, sql, c, remaining); return prepareCommandList(c, sql, remaining);
} }
} }
return c; return c;
...@@ -696,6 +700,46 @@ public class Parser { ...@@ -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. * Parse the statement, but don't prepare it for execution.
* *
...@@ -727,12 +771,12 @@ public class Parser { ...@@ -727,12 +771,12 @@ public class Parser {
} else { } else {
expectedList = null; expectedList = null;
} }
parameters = Utils.newSmallArrayList(); parameters = suppliedParameters != null ? suppliedParameters : Utils.<Parameter>newSmallArrayList();
indexedParameterList = suppliedParameterList;
currentSelect = null; currentSelect = null;
currentPrepared = null; currentPrepared = null;
createView = null; createView = null;
recompileAlways = false; recompileAlways = false;
indexedParameterList = suppliedParameterList;
read(); read();
return parsePrepared(); return parsePrepared();
} }
...@@ -3834,6 +3878,7 @@ public class Parser { ...@@ -3834,6 +3878,7 @@ public class Parser {
if (p == null) { if (p == null) {
p = new Parameter(index); p = new Parameter(index);
indexedParameterList.set(index, p); indexedParameterList.set(index, p);
parameters.add(p);
} }
read(); read();
} else { } else {
...@@ -3843,8 +3888,8 @@ public class Parser { ...@@ -3843,8 +3888,8 @@ public class Parser {
.get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS); .get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
} }
p = new Parameter(parameters.size()); p = new Parameter(parameters.size());
}
parameters.add(p); parameters.add(p);
}
return p; return p;
} }
......
...@@ -199,6 +199,7 @@ public class TestPreparedStatement extends TestDb { ...@@ -199,6 +199,7 @@ public class TestPreparedStatement extends TestDb {
testColumnMetaDataWithEquals(conn); testColumnMetaDataWithEquals(conn);
testColumnMetaDataWithIn(conn); testColumnMetaDataWithIn(conn);
testValueResultSet(conn); testValueResultSet(conn);
testMultipleStatements(conn);
conn.close(); conn.close();
testPreparedStatementWithLiteralsNone(); testPreparedStatementWithLiteralsNone();
testPreparedStatementWithIndexedParameterAndLiteralsNone(); testPreparedStatementWithIndexedParameterAndLiteralsNone();
...@@ -1758,4 +1759,31 @@ public class TestPreparedStatement extends TestDb { ...@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论