1616
1717package org .labkey .api .data ;
1818
19- import org .apache .logging .log4j .LogManager ;
2019import org .apache .logging .log4j .Logger ;
2120import org .jetbrains .annotations .NotNull ;
2221import org .jetbrains .annotations .Nullable ;
2524import org .labkey .api .data .dialect .StatementWrapper ;
2625import org .labkey .api .util .ExceptionUtil ;
2726import org .labkey .api .util .MemTracker ;
27+ import org .labkey .api .util .logging .LogHelper ;
2828import org .springframework .dao .ConcurrencyFailureException ;
2929import org .springframework .jdbc .BadSqlGrammarException ;
3030
4040import static org .labkey .api .util .ExceptionUtil .CALCULATED_COLUMN_SQL_TAG ;
4141
4242/**
43- * Selector that is driven by SQL, which subclasses can control how it's interpreted (LabKey SQL, raw DB SQL, etc)
43+ * Selector that is driven by SQL, which subclasses can control how it's interpreted (LabKey SQL, raw DB SQL, etc. )
4444 */
4545public abstract class SqlExecutingSelector <FACTORY extends SqlFactory , SELECTOR extends SqlExecutingSelector <FACTORY , SELECTOR >> extends BaseSelector <SELECTOR >
4646{
47+ private static final Logger LOGGER = LogHelper .getLogger (SqlExecutingSelector .class , "Log warnings about SQL exceptions" );
48+
4749 int _maxRows = Table .ALL_ROWS ;
4850 protected long _offset = Table .NO_OFFSET ;
4951 @ Nullable Map <String , Object > _namedParameters = null ;
5052 private ConnectionFactory _connectionFactory = super ::getConnection ;
53+ private Integer _fetchSize = null ; // By default, use the standard fetch size
5154
52- private @ Nullable AsyncQueryRequest _asyncRequest = null ;
55+ private @ Nullable AsyncQueryRequest <?> _asyncRequest = null ;
5356 private @ Nullable StackTraceElement [] _loggingStacktrace = null ;
5457 private final QueryLogging _queryLogging ;
55- private static final Logger LOGGER = LogManager .getLogger (SqlExecutingSelector .class );
5658
5759 // SQL factory used for the duration of a single query execution. This allows reuse of instances, since query-specific
5860 // optimizations won't mutate the ExecutingSelector's externally set state.
@@ -111,6 +113,16 @@ public SELECTOR setJdbcCaching(boolean cache)
111113 return getThis ();
112114 }
113115
116+ /**
117+ * Set a ResultSet fetch size that differs from the default value (1,000 rows on PostgreSQL). This is normally a
118+ * fine fetch size, but not when dealing with rows containing large TEXT or BYTEA columns.
119+ */
120+ public SELECTOR setFetchSize (int fetchSize )
121+ {
122+ _fetchSize = fetchSize ;
123+ return getThis ();
124+ }
125+
114126 @ Override
115127 protected ResultSetFactory getStandardResultSetFactory ()
116128 {
@@ -282,7 +294,7 @@ protected boolean exists(FACTORY factory)
282294 }
283295
284296
285- void setAsyncRequest (@ Nullable AsyncQueryRequest asyncRequest )
297+ void setAsyncRequest (@ Nullable AsyncQueryRequest <?> asyncRequest )
286298 {
287299 _asyncRequest = asyncRequest ;
288300
@@ -291,7 +303,7 @@ void setAsyncRequest(@Nullable AsyncQueryRequest asyncRequest)
291303 }
292304
293305 @ Nullable
294- private AsyncQueryRequest getAsyncRequest ()
306+ private AsyncQueryRequest <?> getAsyncRequest ()
295307 {
296308 return _asyncRequest ;
297309 }
@@ -466,7 +478,7 @@ public <T> T handleResultSet(ResultSetHandler<T> handler)
466478 }
467479 }
468480
469- private ResultSet executeQuery (Connection conn , SQLFragment sqlFragment , boolean scrollable , @ Nullable AsyncQueryRequest asyncRequest , @ Nullable Integer statementMaxRows ) throws SQLException
481+ private ResultSet executeQuery (Connection conn , SQLFragment sqlFragment , boolean scrollable , @ Nullable AsyncQueryRequest <?> asyncRequest , @ Nullable Integer statementMaxRows ) throws SQLException
470482 {
471483 List <Object > parameters = sqlFragment .getParams ();
472484 String sql = sqlFragment .getSQL ();
@@ -475,13 +487,13 @@ private ResultSet executeQuery(Connection conn, SQLFragment sqlFragment, boolean
475487 if (null == parameters || parameters .isEmpty ())
476488 {
477489 Statement stmt = conn .createStatement (scrollable ? ResultSet .TYPE_SCROLL_INSENSITIVE : ResultSet .TYPE_FORWARD_ONLY , ResultSet .CONCUR_READ_ONLY );
478- initializeStatement (conn , stmt , asyncRequest , statementMaxRows );
490+ initializeStatement (stmt , asyncRequest , statementMaxRows );
479491 rs = stmt .executeQuery (sql );
480492 }
481493 else
482494 {
483495 PreparedStatement stmt = conn .prepareStatement (sql , scrollable ? ResultSet .TYPE_SCROLL_INSENSITIVE : ResultSet .TYPE_FORWARD_ONLY , ResultSet .CONCUR_READ_ONLY );
484- initializeStatement (conn , stmt , asyncRequest , statementMaxRows );
496+ initializeStatement (stmt , asyncRequest , statementMaxRows );
485497
486498 try (Parameter .ParameterList jdbcParameters = new Parameter .ParameterList ())
487499 {
@@ -499,14 +511,19 @@ private ResultSet executeQuery(Connection conn, SQLFragment sqlFragment, boolean
499511 return rs ;
500512 }
501513
502- private void initializeStatement (Connection conn , Statement stmt , @ Nullable AsyncQueryRequest asyncRequest , @ Nullable Integer statementMaxRows ) throws SQLException
514+ private void initializeStatement (Statement stmt , @ Nullable AsyncQueryRequest <?> asyncRequest , @ Nullable Integer statementMaxRows ) throws SQLException
503515 {
504516 // Don't set max rows if null or special ALL_ROWS value (we're assuming statement.getMaxRows() defaults to 0, though this isn't actually documented...)
505517 if (null != statementMaxRows && Table .ALL_ROWS != statementMaxRows )
506518 {
507519 stmt .setMaxRows (statementMaxRows == Table .NO_ROWS ? 1 : statementMaxRows );
508520 }
509521
522+ if (null != _fetchSize )
523+ {
524+ stmt .setFetchSize (_fetchSize );
525+ }
526+
510527 if (asyncRequest != null )
511528 {
512529 asyncRequest .setStatement (stmt );
0 commit comments