This is an automated email from the ASF dual-hosted git repository.

paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sedona-db.git


The following commit(s) were added to refs/heads/main by this push:
     new e8147cf5 fix(rust/sedona): Fix panic when displaying very long content 
(#565)
e8147cf5 is described below

commit e8147cf54bd8b548ba0796fa1636490e8a5dc3b5
Author: Dewey Dunnington <[email protected]>
AuthorDate: Mon Feb 9 17:21:33 2026 -0600

    fix(rust/sedona): Fix panic when displaying very long content (#565)
    
    Co-authored-by: Copilot <[email protected]>
---
 rust/sedona/src/show.rs | 90 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 73 insertions(+), 17 deletions(-)

diff --git a/rust/sedona/src/show.rs b/rust/sedona/src/show.rs
index 08ac5c96..01376281 100644
--- a/rust/sedona/src/show.rs
+++ b/rust/sedona/src/show.rs
@@ -92,6 +92,12 @@ pub struct DisplayTableOptions<'a> {
     pub display_mode: DisplayMode,
 }
 
+/// Arithmetic here uses saturating_add to avoid panics for overflow; however,
+/// comfy table doesn't usually use this and if we actually max out any values
+/// we pass to it we may end up causing a panic. We use a conservative value
+/// of 32,000 characters, which should be enough for just about anybody.
+const WIDTH_MAX: u16 = u16::MAX / 2;
+
 impl DisplayTableOptions<'static> {
     /// Create new options for a non-interactive context
     pub fn new() -> Self {
@@ -248,7 +254,7 @@ impl<'a> DisplayTable<'a> {
     pub fn write<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
         let mut table = Table::new();
         table.set_content_arrangement(ContentArrangement::Dynamic);
-        table.set_width(self.options.table_width);
+        table.set_width(self.options.table_width.min(WIDTH_MAX));
 
         match self.options.display_mode {
             DisplayMode::ASCII => {
@@ -317,29 +323,32 @@ impl<'a> DisplayTable<'a> {
     /// number of characters in other columns (based on user options).
     fn minimum_width(&self) -> Result<u16> {
         // Leftmost border
-        let mut total_size = 1;
-        let mut hidden_count = 0;
+        let mut total_size: u16 = 1;
+        let mut hidden_count: u16 = 0;
 
         for col in &self.columns {
             // Padding on both sides plus separator
-            total_size += 3;
+            total_size = total_size.saturating_add(3);
             match col.constraint(&self.options)? {
-                ColumnConstraint::Hidden => hidden_count += 1,
-                ColumnConstraint::Absolute(Width::Fixed(width)) => total_size 
+= width,
-                ColumnConstraint::LowerBoundary(Width::Fixed(width)) => 
total_size += width,
-                _ => {
-                    total_size += 0;
+                ColumnConstraint::Hidden => hidden_count = 
hidden_count.saturating_add(1),
+                ColumnConstraint::Absolute(Width::Fixed(width)) => {
+                    total_size = total_size.saturating_add(width)
+                }
+                ColumnConstraint::LowerBoundary(Width::Fixed(width)) => {
+                    total_size = total_size.saturating_add(width)
                 }
+                _ => {}
             }
         }
 
         if hidden_count > 0 {
             // Exactly one padding + separator + truncation indicator
-            total_size += 3 + self
-                .truncation_indicator()
-                .len()
-                .try_into()
-                .unwrap_or(u16::MAX);
+            total_size = total_size.saturating_add(3).saturating_add(
+                self.truncation_indicator()
+                    .len()
+                    .try_into()
+                    .unwrap_or(u16::MAX),
+            );
         }
 
         Ok(total_size)
@@ -432,11 +441,14 @@ impl DisplayColumn {
         let is_numeric = ArgMatcher::is_numeric();
         if is_numeric.match_type(&self.sedona_type) {
             Ok(ColumnConstraint::LowerBoundary(Width::Fixed(
-                content_width + padding_width,
+                content_width.saturating_add(padding_width).min(WIDTH_MAX),
             )))
         } else {
             Ok(ColumnConstraint::LowerBoundary(Width::Fixed(
-                options.min_column_width.min(total_width + padding_width),
+                options
+                    .min_column_width
+                    .min(total_width.saturating_add(padding_width))
+                    .min(WIDTH_MAX),
             )))
         }
     }
@@ -522,7 +534,12 @@ impl DisplayColumn {
     /// Create a cell for this column. This is where alignment for numeric
     /// output is applied.
     fn cell<T: ToString>(&self, content: T) -> Cell {
-        let cell = Cell::new(content).set_delimiter('\0');
+        // Never return content longer than WIDTH_MAX because comfy table may
+        // do arithmetic with it without checking for overflow.
+        let content_str = content.to_string();
+        let content_safe: String = content_str.chars().take(WIDTH_MAX as 
usize).collect();
+
+        let cell = Cell::new(content_safe).set_delimiter('\0');
         let is_numeric = ArgMatcher::is_numeric();
         if is_numeric.match_type(&self.sedona_type) {
             cell.set_alignment(CellAlignment::Right)
@@ -697,6 +714,45 @@ mod test {
         );
     }
 
+    #[test]
+    fn render_content_longer_than_u16_max() {
+        let options = DisplayTableOptions::new();
+
+        let short_chars: ArrayRef =
+            arrow_array::create_array!(Utf8, [Some("abcd"), Some("efgh"), 
None]);
+
+        let really_long_string = "a".repeat(u16::MAX as usize + 1);
+        let long_chars: ArrayRef = arrow_array::create_array!(
+            Utf8,
+            [
+                Some("you see, what happened was"),
+                Some(&really_long_string),
+                None
+            ]
+        );
+
+        let cols = vec![
+            ("shrt", (SedonaType::Arrow(DataType::Utf8), short_chars)),
+            (
+                "exceedingly_long",
+                (SedonaType::Arrow(DataType::Utf8), long_chars),
+            ),
+        ];
+
+        assert_eq!(
+            render_cols(cols, options.clone()),
+            vec![
+                
"+------+-------------------------------------------------------------------------------------------+",
+                "| shrt |                                      
exceedingly_long                                     |",
+                
"+------+-------------------------------------------------------------------------------------------+",
+                "| abcd | you see, what happened was                           
                                     |",
+                "| efgh | 
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...
 |",
+                "|      |                                                      
                                     |",
+                
"+------+-------------------------------------------------------------------------------------------+"
+            ]
+        );
+    }
+
     #[rstest]
     #[case(WKB_GEOMETRY)]
     #[case(WKB_VIEW_GEOMETRY)]

Reply via email to