Many thanks to Knut Anders and Mike for their respective replies.
mike> in the above case Derby figured out order by c2 could be done using
mike> index on (c1, c2) because c1 was fixed by where c1=1. So I assume
mike> the query plan did sort elimination in favor of the index scan.
Yes, that's exactly correct. The optimizer chose a "sort avoidance" plan
because it noticed that the index had the correct ordering (and thus no sorting
is required if we use the index). Then in OptimizerImpl.rememberBestCost(...)
we see that a sort avoidance plan was chosen, so we call "sortNotNeeded()" on
the OrderByList, per:
/* Remember if a sort is not needed for this plan */
if (requiredRowOrdering != null)
{
if (planType == Optimizer.SORT_AVOIDANCE_PLAN)
requiredRowOrdering.sortNotNeeded();
else
requiredRowOrdering.sortNeeded();
}
The call to "sortNotNeeded()" effectively means that we will call
"markOrderingDependent()" on the IndexRowToBaseRowNode, which in turn means that
the Index Scan will *not* bulk fetch rows; it will get them *one* at a time.
This then correlates with Knut Anders observation:
knut> if you do an index scan, you automatically get the rows in the
knut> correct order and you only need one pass to return the results.
knut> When the first row is returned, the underlying scan has probably
knut> only seen that one row, so any updates to other rows will be seen.
So I think we have a pretty clear idea of *why* the updates are seen with an
Index Scan (and not with a Table Scan)--which is great.
The remaining question is whether or not this is correct. I'm hoping Knut
Anders is right when he wrote:
knut> there's no guarantee (I think) as to when the rows are actually read.
knut> But I'll leave it to the scholars to find the chapter and verse in the
knut> appropriate spec... ;)
That's what I'm looking for :) Note that I was able to reproduce this behavior
with a JDBC program, inlined at the end of this email. That program uses the
Connection.createStatement() method, which means that result sets from that
statement should default to TYPE_FORWARD_ONLY (as opposed to SCROLL_INSENSITIVE
or SCROLL_SENSITIVE).
So it seems like the question is: should a "forward only" result set be
sensitive to updates to rows that it hasn't read yet? Or is this documented
somewhere in JDBC as "not guaranteed" (or whatever the correct term is)? Unless
there is doc saying that such a thing leads to undefined behavior (per Knut
Anders' suggestion), it seems odd (to me) that the "sensitivity" of a
forward-only result set apparently depends on the underlying Derby scan type...
But for now this is a "good enough" answer. I don't think this blocks
DERBY-2805, it was just a question that came from investigation of that issue.
Thank you again to Mike and Knut Anders for the feedback; it's much appreciated!
Army
--------
JDBC repro for the original question (called "d2805" because that was the issue
which prompted the question):
import java.sql.*;
public class d2805
{
public static void main(String [] args)
{
try {
(new d2805()).go(args);
} catch (Exception e) {
System.out.println("-=- OOPS: " + e.getMessage());
}
System.out.println("\n[ Done. ]\n");
}
private void go(String [] args) throws Exception
{
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
Connection conn = DriverManager.getConnection(
"jdbc:derby:dbdb;create=true");
/* Note that default Statement returns ResultSets which are
* TYPE_FORWARD_ONLY and CONCUR_READ_ONLY.
*/
Statement st = conn.createStatement();
try {
st.execute("drop table str");
} catch (SQLException se) {}
st.execute("create table str(c1 int, c2 int, c3 int)");
st.execute("insert into str values (1, 1, 2), (1, 2, 3), " +
"(1, 3, 4), (1, 4, 5)");
st.execute("create index str1 on str(c1, c2)");
System.out.println("\nRunning w/ Index Scan:\n");
runQuery("str1", st, conn);
System.out.println("\nRunning w/ Table Scan:\n");
runQuery("null", st, conn);
conn.close();
}
private void runQuery(String index, Statement st, Connection conn)
throws SQLException
{
conn.setAutoCommit(false);
ResultSet rs = st.executeQuery(
"select c2, c3 from str --DERBY-PROPERTIES index=" + index +
"\nwhere c1 = 1 order by c2");
rs.next();
System.out.println("Row 1: " + rs.getInt(1) + ", " + rs.getInt(2));
Statement st2 = conn.createStatement();
st2.executeUpdate("update str set c2 = 4 where c2 = 2");
st2.close();
int row = 2;
while (rs.next())
{
System.out.println("Row " + (row++) + ": " +
rs.getInt(1) + ", " + rs.getInt(2));
}
rs.close();
conn.rollback();
}
}