@@ -264,7 +264,7 @@ public SqlExecute(ContainerUser ctx, UserSchema schema, String sql)
264264 this .format = Format .tsv ;
265265 }
266266
267- SqlExecute page (Integer offset , Integer limit )
267+ SqlExecute page (Integer offset , int limit )
268268 {
269269 this .offset = offset ;
270270 this .limit = limit ;
@@ -335,41 +335,44 @@ void getStringData(Results rs, ArrayList<String> out) throws SQLException
335335 }
336336 }
337337
338- void writeResults_text (Writer out , Results rs , String sep , String eol ) throws IOException , SQLException
338+ int writeResults_text (Writer out , Results rs , int limit , String sep , String eol ) throws IOException , SQLException
339339 {
340340 initWriter (rs );
341- final int count = rs .getMetaData ().getColumnCount ();
341+ final int columnCount = rs .getMetaData ().getColumnCount ();
342342
343343 // meta-meta-data
344344 out .write ("18.2" +sep +"name" +sep +"jdbcType" +eol );
345345
346- for (int column = 1 ; column <= count ; column ++)
346+ for (int column = 1 ; column <= columnCount ; column ++)
347347 {
348348 out .write (rs .getColumn (column ).getName ());
349- out .write (column == count ? eol : sep );
349+ out .write (column == columnCount ? eol : sep );
350350 }
351351
352- for (int column = 1 ; column <= count ; column ++)
352+ for (int column = 1 ; column <= columnCount ; column ++)
353353 {
354354 int index = column -1 ;
355355 out .write (types [index ].name ());
356- out .write (column == count ? eol : sep );
356+ out .write (column == columnCount ? eol : sep );
357357 }
358358
359- ArrayList <String > values = new ArrayList <>(count );
359+ ArrayList <String > values = new ArrayList <>(columnCount );
360360
361- while (rs .next ())
361+ int count = 0 ;
362+ while (--limit >= 0 && rs .next ())
362363 {
363364 getStringData (rs , values );
364- for (int index = 0 ; index < count ; index ++)
365+ for (int index = 0 ; index < columnCount ; index ++)
365366 {
366367 String s = values .get (index );
367368 if (null != s )
368369 out .write (s );
369- out .write (index == count - 1 ? eol : sep );
370+ out .write (index == columnCount - 1 ? eol : sep );
370371 }
372+ count ++;
371373 }
372374 out .flush ();
375+ return count ;
373376 }
374377
375378 /**
@@ -387,35 +390,36 @@ void writeResults_text(Writer out, Results rs, String sep, String eol) throws IO
387390 * implementation is generating strings inside out.write(). So this is probably not much different from a GC
388391 * perspective.
389392 */
390- void writeResults_compact (Writer out , Results rs , String sep , String eol ) throws IOException , SQLException
393+ int writeResults_compact (Writer out , Results rs , int limit , String sep , String eol ) throws IOException , SQLException
391394 {
392395 initWriter (rs );
393- final int count = rs .getMetaData ().getColumnCount ();
396+ final int columnCount = rs .getMetaData ().getColumnCount ();
394397
395398 // meta-meta-data
396399 out .write ("18.2" +sep +"name" +sep +"jdbcType" +eol );
397400
398- for (int column = 1 ; column <= count ; column ++)
401+ for (int column = 1 ; column <= columnCount ; column ++)
399402 {
400403 out .write (rs .getColumn (column ).getName ());
401- out .write (column == count ? eol : sep );
404+ out .write (column == columnCount ? eol : sep );
402405 }
403406
404- for (int index = 0 ; index < count ; index ++)
407+ for (int index = 0 ; index < columnCount ; index ++)
405408 {
406409 out .write (types [index ].name ());
407- out .write (index == count -1 ? eol : sep );
410+ out .write (index == columnCount -1 ? eol : sep );
408411 }
409412
410413 String DITTO = String .valueOf (BS );
411- ArrayList <String > prev = new ArrayList <>(count );
412- ArrayList <String > row = new ArrayList <>(count );
414+ ArrayList <String > prev = new ArrayList <>(columnCount );
415+ ArrayList <String > row = new ArrayList <>(columnCount );
413416
414- while (rs .next ())
417+ int count = 0 ;
418+ while (--limit >= 0 && rs .next ())
415419 {
416420 getStringData (rs , row );
417421
418- for (int index = 0 ; index < count ; index ++)
422+ for (int index = 0 ; index < columnCount ; index ++)
419423 {
420424 String s = row .get (index );
421425 if (null != s && !s .isEmpty ())
@@ -425,63 +429,80 @@ void writeResults_compact(Writer out, Results rs, String sep, String eol) throws
425429 else
426430 out .write (s );
427431 }
428- out .write (index == count - 1 ? eol : sep );
432+ out .write (index == columnCount - 1 ? eol : sep );
429433 }
430434 ArrayList <String > t = prev ;
431435 prev = row ;
432436 row = t ;
437+ count ++;
433438 }
434439 out .flush ();
440+ return count ;
435441 }
436442
437443 /// export a Result set using RFC4180 formatting
438444 /// use PageFlowUtil.joinValuesWithTabs4180
439- void writeResults_tsv (Writer out , Results rs ) throws IOException , SQLException
445+ int writeResults_tsv (Writer out , Results rs , int limit ) throws IOException , SQLException
440446 {
441447 initWriter (rs );
442- final int count = rs .getMetaData ().getColumnCount ();
448+ final int columnCount = rs .getMetaData ().getColumnCount ();
443449
444- List <String > names = new ArrayList <>(count );
445- for (int column = 1 ; column <= count ; column ++)
450+ List <String > names = new ArrayList <>(columnCount );
451+ for (int column = 1 ; column <= columnCount ; column ++)
446452 names .add (rs .getColumn (column ).getName ());
447453 out .write (PageFlowUtil .joinValuesWithTabs4180 (names ));
448454 out .write ('\n' );
449455
450- ArrayList <String > values = new ArrayList <>(count );
456+ ArrayList <String > values = new ArrayList <>(columnCount );
451457
452- while (rs .next ())
458+ int count = 0 ;
459+ while (--limit >= 0 && rs .next ())
453460 {
454461 getStringData (rs , values );
455462 out .write (PageFlowUtil .joinValuesWithTabs4180 (values ));
456463 out .write ('\n' );
464+ count ++;
457465 }
458466 out .flush ();
467+ return count ;
459468 }
460469
470+ public record ExecuteResult (int rows , boolean complete ) {}
461471
462- public void execute (Writer out ) throws SQLException , IOException
472+ public ExecuteResult execute (Writer out ) throws SQLException , IOException
463473 {
464474 schema .checkCanReadSchema ();
465475 var builder = QueryService .get ().getSelectBuilder (schema , sql , true );
466476 if (null != offset )
467477 builder .offset (offset );
468- if (null != limit )
469- builder .maxRows (limit );
478+ int maxPrint = Integer .MAX_VALUE ;
479+ if (null != limit && limit < Integer .MAX_VALUE )
480+ {
481+ builder .maxRows (limit + 1 );
482+ maxPrint = limit ;
483+ }
470484
471485 try (Results rs = builder .select (false , parameterMap ))
472486 {
487+ int count ;
488+ boolean complete = true ;
473489 switch (format )
474490 {
475491 case tsv :
476- writeResults_tsv (out , rs );
492+ count = writeResults_tsv (out , rs , maxPrint );
477493 break ;
478494 case split :
479- writeResults_text (out , rs , sep , eol );
495+ count = writeResults_text (out , rs , maxPrint , sep , eol );
480496 break ;
481497 case compact :
482- writeResults_compact (out , rs , sep , eol );
498+ count = writeResults_compact (out , rs , maxPrint , sep , eol );
483499 break ;
500+ default :
501+ throw new IllegalArgumentException ("Unknown format: " + format );
484502 }
503+ while (rs .next ())
504+ complete = false ;
505+ return new ExecuteResult (count , complete );
485506 }
486507 }
487508 }
@@ -835,5 +856,42 @@ public void testTruncation() throws Exception
835856 assertEquals ("Bob" , lines [2 ]); // 3 == 3: not truncated (boundary)
836857 assertEquals ("Car…[truncated]" , lines [3 ]); // 5 > 3: truncated
837858 }
859+
860+ private SqlExecute .ExecuteResult executeWithLimit (int limit ) throws Exception
861+ {
862+ User user = TestContext .get ().getUser ();
863+ UserSchema listsSchema = (UserSchema ) DefaultSchema .get (user , _folder ).getSchema ("lists" );
864+ ContainerUser cu = ContainerUser .create (_folder , user );
865+ var execute = new SqlExecute (cu , listsSchema , "SELECT Name FROM " + LIST_NAME + " ORDER BY Name" )
866+ .page (0 , limit );
867+ return execute .execute (new StringWriter ());
868+ }
869+
870+ @ Test
871+ public void testLimit_lessThanRowCount () throws Exception
872+ {
873+ // 3 rows in table, limit=2 → incomplete result
874+ var result = executeWithLimit (2 );
875+ assertEquals (2 , result .rows ());
876+ assertFalse ("Expected more rows to be available" , result .complete ());
877+ }
878+
879+ @ Test
880+ public void testLimit_equalToRowCount () throws Exception
881+ {
882+ // 3 rows in table, limit=3 → complete result
883+ var result = executeWithLimit (3 );
884+ assertEquals (3 , result .rows ());
885+ assertTrue ("Expected result to be complete" , result .complete ());
886+ }
887+
888+ @ Test
889+ public void testLimit_greaterThanRowCount () throws Exception
890+ {
891+ // 3 rows in table, limit=10 → complete result
892+ var result = executeWithLimit (10 );
893+ assertEquals (3 , result .rows ());
894+ assertTrue ("Expected result to be complete" , result .complete ());
895+ }
838896 }
839897}
0 commit comments