Daniel John Debrunner wrote:
Bernt M. Johnsen wrote:
The patch looks sound. I'll commit when I have run derbyall and
experimented a bit on my own.
Is there an overview of what the patch does, any implementation details?
Here:
The purpose of the patch is to allow a statement to be updatable without
having to specify "FOR UPDATE".
My first look in the code indicated that I could do the fix by changing
only one line of code:
org.apache.derby.impl.sql.compile.CursorNode.java, bind()
if (updateMode == UNSPECIFIED) {
>> updateMode = determineUpdateMode(dataDictionary);
<< updateMode = READ_ONLY;
}
(UNSPECIFED here means that the statement does not contain "FOR UPDATE"
or "FOR READ ONLY")
determineUpdateMode() checks if the statement can be updatable (i.e does
not contain a join).
However, just changing this single line of code, would cause some
side-effects if the user does not plan to do update:
a: The query plan would contain updateNodes, and it would probably not
be optimal if using indexes.
b: The updateMode is used to determine the lockmode, so even a read-only
select would use updatelocks.
To prevent these side-effects, the idea is to provide the
CursorNode.bind() method with some information about the concurrency
mode for the statement. A JDBC Statement can be created with two modes
of concurrency: "ResultSet.CONCUR_READ_ONLY" or
"ResultSet.CONCUR_UPDATABLE". The default is ResultSet.CONCUR_READ_ONLY.
The CursorNode code has access to a LanguageConnectionContext and
StatementContext. The patch makes information about the concurrency mode
for the statement part of the StatementContext by using a flag, and it
is passed down the layers whenever a statement is prepared.
This is done by adding a parameter to LanaguageConnectionContext.
prepareInternalStatement(..) with the flag.
The CursorNode then uses the flag to determine the update mode:
CursorNode.java, bind()
if (updateMode == UNSPECIFIED) {
if (getLanguageConnectionContext().
getStatementContext().isForReadOnly()) {
updateMode = READ_ONLY;
} else {
updateMode = determineUpdateMode(dataDictionary);
}
}
Statement caching:
Statements with identical SQL strings and identical schema on the same
connection are cached in the GenericLangageConnectionContext
(implementation of LanguageConnectionContext). This is done by
a, in the prepareInternalStatement(..) method a GenericStatement object
is created taking the parameters for schema and sql string.
b, This object is later used to lookup a GenericPreparedStatement from
the cache.
Since now, a "SELECT * FROM T" can produce two different query plans,
depending on the concurrency mode, the flag for concurrency mode is made
part of the GenericStatement object, and its identiy (by modifying the
equals() method). This ensures that the correct cached
GenericPreparedStatement is looked up.
Side-effects and user impact:
The patch has been designed to not affect the current behaviour of
"SELECT * FROM T FOR UPDATE" or "SELECT * FROM T FOR READ ONLY"
statements regardless of concurrency mode.
It also provides the exact same behaviour as before if you do a "SELECT
* FROM T" with concurrency mode CONCUR_READ_ONLY.
The patch only affect "SELECT * FROM T" if the concurrency mode is
CONCUR_UPDATABLE, by making the the cursor updatable from the ResultSet,
and with positioned updates.
-- Andreas
Dan.