-
Notifications
You must be signed in to change notification settings - Fork 852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: fetch size for RefCursor #2976
base: master
Are you sure you want to change the base?
Conversation
// ignore request to set fetchSize to 0 if it is currently > 0 | ||
if (fetchSize > 0 && rows == 0) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please clarify why ignore the new fetch size?
I guess users might want to adjust fetchSize as they fetch, so it might be unexpected to ignore the new values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you set the fetchSize to 0 here we will stop fetching more data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should treat fetchSize
like a hint rather than an "exact value to use for fetching". Then 0
would not cause "0 rows to fetch"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The initial value for fetchSize
is 0
, so ignoring setFetchSize(0)
sounds arbitrary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fetch size of zero is specifically called out as special If the fetch size specified is zero, the JDBC driver ignores the value and is free to make its own best guess as to what the fetch size should be.
if (refCursorName == null || refCursorName.isEmpty()) { | ||
castNonNull(resultCursor, "resultCursor"); | ||
connection.getQueryExecutor() | ||
.fetch(resultCursor, new CursorResultHandler(), fetchRows, adaptiveFetch); | ||
// .fetch(...) could update this.cursor, and cursor==null means | ||
// there are no more rows to fetch | ||
|
||
// .fetch(...) could update this.cursor, and cursor==null means | ||
// there are no more rows to fetch | ||
closeRefCursor(); | ||
} else { | ||
StringBuilder sb = new StringBuilder("FETCH FORWARD "); | ||
sb.append(fetchRows); | ||
sb.append(" IN "); | ||
castNonNull(refCursorName, "refCursorName"); | ||
Utils.escapeIdentifier(sb, refCursorName); | ||
final Query cursorForward = connection.getQueryExecutor().createSimpleQuery(sb.toString()); | ||
// pass maxRows=0 since fetchSize has already been corrected to account for maxRows | ||
connection.getQueryExecutor().execute(cursorForward, null, new CursorResultHandler(), 0, fetchSize, QueryExecutor.QUERY_ONESHOT | QueryExecutor.QUERY_SUPPRESS_BEGIN | ||
| QueryExecutor.QUERY_EXECUTE_AS_SIMPLE ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can reuse the existing "extended protocol" fetch to fetch the data rather than build "fetch forward" commands dynamically
This replaces the original PR pgjdbc#925 originally written by @rhavermans It was easier to pull in the changes from his PR instead of rebasing the original. This is entirely his code. We now support setting the fetch size on a RefCursor.
Update docs for new behaviour of refcursor
968e2c4
to
27c842a
Compare
@vlsi I want to include this in the release. Thoughts ? |
if (refCursorName == null || refCursorName.isEmpty()) { | ||
castNonNull(resultCursor, "resultCursor"); | ||
connection.getQueryExecutor() | ||
.fetch(resultCursor, new CursorResultHandler(), fetchRows, adaptiveFetch); | ||
// .fetch(...) could update this.cursor, and cursor==null means | ||
// there are no more rows to fetch | ||
|
||
// .fetch(...) could update this.cursor, and cursor==null means | ||
// there are no more rows to fetch | ||
closeRefCursor(); | ||
} else { | ||
StringBuilder sb = new StringBuilder("FETCH FORWARD "); | ||
sb.append(fetchRows); | ||
sb.append(" IN "); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am afraid this change does not fix the issue.
It uses fetchRows
from the base ResultSet
rather than fetchRows
of the ResultSet
that is associated with refcursor
in question.
In other words:
Imagine we call {? = call test_blob(?)}
.
CallableStatement.execute
would gather call results, and it would execute getObject
at
callResult[j] = rs.getObject(i + 1); |
Then it would delegate to PgResultSet.getObject -> internalGetObject -> getRefCursor
, and it would effectively use fetchSize
from a temporary resultset associated with {? = call test_blob(?)}
.
I believe we should add a test to ensure fetch forward
is triggered at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are the logs from running
public void testFetchSize() throws SQLException {
checkAndRunResult(3);
}
2023-11-16 06:53:04.080 EST [84077] LOG: duration: 0.136 ms execute <unnamed>/C_1: select * from testspg__getRefcursor ($1) as result
2023-11-16 06:53:04.080 EST [84077] DETAIL: parameters: $1 = NULL
2023-11-16 06:53:04.080 EST [84077] LOG: duration: 0.008 ms parse <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.080 EST [84077] LOG: duration: 0.002 ms bind <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.080 EST [84077] LOG: duration: 0.039 ms execute <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.080 EST [84077] LOG: duration: 0.002 ms parse <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.080 EST [84077] LOG: duration: 0.001 ms bind <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.080 EST [84077] LOG: duration: 0.003 ms execute <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.002 ms parse <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.002 ms bind <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.001 ms execute <unnamed>: FETCH FORWARD 3 IN "<unnamed portal 1>"
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.006 ms parse <unnamed>: CLOSE "<unnamed portal 1>"
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.001 ms bind <unnamed>: CLOSE "<unnamed portal 1>"
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.002 ms execute <unnamed>: CLOSE "<unnamed portal 1>"
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.005 ms parse S_2: COMMIT
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.002 ms bind S_2: COMMIT
2023-11-16 06:53:04.081 EST [84077] LOG: duration: 0.006 ms execute S_2: COMMIT
if (fetchSize != null) { | ||
call.setFetchSize(fetchSize); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frankly speaking, I would not expect call.setFetchSize
to be inherited when fetching the refcursor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why not ?
Statement.setFetchSize() docs say
Gives the JDBC driver a hint as to the number of rows that should be fetched from the database when more rows are needed for ResultSet objects generated by this Statement. If the value specified is zero, then the hint is ignored. The default value is zero.
Params:
rows – the number of rows to fetch
Throws:
SQLException – if a database access error occurs, this method is called on a closed Statement or the condition rows >= 0 is not satisfied.
How else would they be used ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResultSet.setFetchSize would be ignored though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, however it is allowed.
Gives the JDBC driver a hint as to the number of rows that should be fetched from the database when more rows are needed for this ResultSet object. If the fetch size specified is zero, the JDBC driver ignores the value and is free to make its own best guess as to what the fetch size should be. The default value is set by the Statement object that created the result set. The fetch size may be changed at any time.
I don't see a way to change it once we have the statement. Does that currently work with portals ?
This replaces the original PR #925 originally written by @rhavermans It was easier to pull in the changes from his PR instead of rebasing the original. This is entirely his code.
We now support setting the fetch size on a RefCursor.
All Submissions:
New Feature Submissions:
./gradlew autostyleCheck checkstyleAll
pass ?Changes to Existing Features: