tconbeer / harlequin

The SQL IDE for Your Terminal.
https://harlequin.sh
MIT License
3.55k stars 77 forks source link

Harlequin crashes with ArrowInvalid if databases return multiple types in one column #453

Closed tconbeer closed 7 months ago

tconbeer commented 7 months ago

I think this is only an issue with sqlite, since it's untyped.

harlequin -a sqlite

then

select 1000 union all select 'hi'

Crashes with:

╭───────────────────────────────────────── Traceback (most recent call last) ──────────────────────────────────────────╮
│ /home/tco/open/harlequin/src/harlequin/app.py:479 in load_tables                                                     │
│                                                                                                                      │
│   476 │   @on(ResultsFetched)                                                                                        │
│   477 │   async def load_tables(self, message: ResultsFetched) -> None:                                              │
│   478 │   │   for id_, (cols, data, query_text) in message.data.items():                                             │
│ ❱ 479 │   │   │   table = await self.results_viewer.push_table(                                                      │
│   480 │   │   │   │   table_id=id_,                                                                                  │
│   481 │   │   │   │   column_labels=cols,                                                                            │
│   482 │   │   │   │   data=data,  # type: ignore                                                                     │
│                                                                                                                      │
│ ╭───────────────────────────── locals ──────────────────────────────╮                                                │
│ │       cols = [('1000', '##')]                                     │                                                │
│ │       data = [(1000,), ('hi',)]                                   │                                                │
│ │        id_ = 't8774243273668'                                     │                                                │
│ │    message = ResultsFetched()                                     │                                                │
│ │ query_text = "select 1000\nunion all select 'hi'"                 │                                                │
│ │       self = Harlequin(title='Harlequin', classes={'-dark-mode'}) │                                                │
│ ╰───────────────────────────────────────────────────────────────────╯                                                │
│                                                                                                                      │
│ /home/tco/open/harlequin/src/harlequin/components/results_viewer.py:80 in push_table                                 │
│                                                                                                                      │
│    77 │   │   │   self._format_column_label(col_name, col_type)                                                      │
│    78 │   │   │   for col_name, col_type in column_labels                                                            │
│    79 │   │   ]                                                                                                      │
│ ❱  80 │   │   table = ResultsTable(                                                                                  │
│    81 │   │   │   id=table_id,                                                                                       │
│    82 │   │   │   column_labels=formatted_labels,  # type: ignore                                                    │
│    83 │   │   │   data=data,                                                                                         │
│                                                                                                                      │
│ ╭────────────────── locals ──────────────────╮                                                                       │
│ │    column_labels = [('1000', '##')]        │                                                                       │
│ │             data = [(1000,), ('hi',)]      │                                                                       │
│ │ formatted_labels = ['1000 [#777777]##[/]'] │                                                                       │
│ │             self = ResultsViewer()         │                                                                       │
│ │         table_id = 't8774243273668'        │                                                                       │
│ ╰────────────────────────────────────────────╯                                                                       │
│                                                                                                                      │
│ /home/tco/open/textual-fastdatatable/src/textual_fastdatatable/data_table.py:559 in __init__                         │
│                                                                                                                      │
│    556 │   │   │   self.backend: DataTableBackend | None = (                                                         │
│    557 │   │   │   │   backend                                                                                       │
│    558 │   │   │   │   if backend is not None                                                                        │
│ ❱  559 │   │   │   │   else create_backend(                                                                          │
│    560 │   │   │   │   │   data, max_rows=max_rows, has_header=(column_labels is None)  # type:                      │
│    561 │   │   │   │   )                                                                                             │
│    562 │   │   │   )                                                                                                 │
│                                                                                                                      │
│ ╭──────────────────────────── locals ────────────────────────────╮                                                   │
│ │                    backend = None                              │                                                   │
│ │                    classes = None                              │                                                   │
│ │              column_labels = ['1000 [#777777]##[/]']           │                                                   │
│ │              column_widths = None                              │                                                   │
│ │ cursor_background_priority = 'renderable'                      │                                                   │
│ │ cursor_foreground_priority = 'css'                             │                                                   │
│ │                cursor_type = 'range'                           │                                                   │
│ │                       data = [(1000,), ('hi',)]                │                                                   │
│ │                   disabled = False                             │                                                   │
│ │              fixed_columns = 0                                 │                                                   │
│ │                 fixed_rows = 0                                 │                                                   │
│ │              header_height = 1                                 │                                                   │
│ │                         id = 't8774243273668'                  │                                                   │
│ │   max_column_content_width = 43                                │                                                   │
│ │                   max_rows = 100000                            │                                                   │
│ │                       name = None                              │                                                   │
│ │                   null_rep = '[dim]∅ null[/]'                  │                                                   │
│ │                       self = ResultsTable(id='t8774243273668') │                                                   │
│ │                show_cursor = True                              │                                                   │
│ │                show_header = True                              │                                                   │
│ │            show_row_labels = True                              │                                                   │
│ │              zebra_stripes = False                             │                                                   │
│ ╰────────────────────────────────────────────────────────────────╯                                                   │
│                                                                                                                      │
│ /home/tco/open/textual-fastdatatable/src/textual_fastdatatable/backend.py:41 in create_backend                       │
│                                                                                                                      │
│    38 │   elif isinstance(data, Sequence) and not data:                                                              │
│    39 │   │   return ArrowBackend(pa.table([]), max_rows=max_rows)                                                   │
│    40 │   elif isinstance(data, Sequence) and _is_iterable(data[0]):                                                 │
│ ❱  41 │   │   return ArrowBackend.from_records(data, max_rows=max_rows, has_header=has_header)                       │
│    42 │   elif (                                                                                                     │
│    43 │   │   isinstance(data, Mapping)                                                                              │
│    44 │   │   and isinstance(next(iter(data.keys())), str)                                                           │
│                                                                                                                      │
│ ╭──────────── locals ─────────────╮                                                                                  │
│ │       data = [(1000,), ('hi',)] │                                                                                  │
│ │ has_header = False              │                                                                                  │
│ │   max_rows = 100000             │                                                                                  │
│ ╰─────────────────────────────────╯                                                                                  │
│                                                                                                                      │
│ /home/tco/open/textual-fastdatatable/src/textual_fastdatatable/backend.py:218 in from_records                        │
│                                                                                                                      │
│   215 │   │   max_rows: int | None = None,                                                                           │
│   216 │   ) -> "ArrowBackend":                                                                                       │
│   217 │   │   pydict = cls._pydict_from_records(records, has_header)                                                 │
│ ❱ 218 │   │   return cls.from_pydict(pydict, max_rows=max_rows)                                                      │
│   219 │                                                                                                              │
│   220 │   @property                                                                                                  │
│   221 │   def source_row_count(self) -> int:                                                                         │
│                                                                                                                      │
│ ╭───────────────────────────── locals ──────────────────────────────╮                                                │
│ │        cls = <class 'textual_fastdatatable.backend.ArrowBackend'> │                                                │
│ │ has_header = False                                                │                                                │
│ │   max_rows = 100000                                               │                                                │
│ │     pydict = {'f0': [1000, 'hi']}                                 │                                                │
│ │    records = [(1000,), ('hi',)]                                   │                                                │
│ ╰───────────────────────────────────────────────────────────────────╯                                                │
│                                                                                                                      │
│ /home/tco/open/textual-fastdatatable/src/textual_fastdatatable/backend.py:207 in from_pydict                         │
│                                                                                                                      │
│   204 │   def from_pydict(                                                                                           │
│   205 │   │   cls, data: Mapping[str, Sequence[Any]], max_rows: int | None = None                                    │
│   206 │   ) -> "ArrowBackend":                                                                                       │
│ ❱ 207 │   │   tbl = pa.Table.from_pydict(dict(data))                                                                 │
│   208 │   │   return cls(tbl, max_rows=max_rows)                                                                     │
│   209 │                                                                                                              │
│   210 │   @classmethod                                                                                               │
│                                                                                                                      │
│ ╭──────────────────────────── locals ─────────────────────────────╮                                                  │
│ │      cls = <class 'textual_fastdatatable.backend.ArrowBackend'> │                                                  │
│ │     data = {'f0': [1000, 'hi']}                                 │                                                  │
│ │ max_rows = 100000                                               │                                                  │
│ ╰─────────────────────────────────────────────────────────────────╯                                                  │
│                                                                                                                      │
│ in pyarrow.lib._Tabular.from_pydict:1812                                                                             │
│                                                                                                                      │
│ in pyarrow.lib._from_pydict:5275                                                                                     │
│                                                                                                                      │
│ in pyarrow.lib.asarray:374                                                                                           │
│                                                                                                                      │
│ in pyarrow.lib.array:344                                                                                             │
│                                                                                                                      │
│ in pyarrow.lib._sequence_to_array:42                                                                                 │
│                                                                                                                      │
│ in pyarrow.lib.pyarrow_internal_check_status:154                                                                     │
│                                                                                                                      │
│ in pyarrow.lib.check_status:91                                                                                       │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
ArrowInvalid: Could not convert 'hi' with type str: tried to convert to int64