Repository: ibis-project/ibis

Issue:
rowid is not supported in pandas backends
### What happened?

I'm trying to add support for ibis to hvPlot/ HoloViews. When I run the below code I get `ibis.common.exceptions.UnsupportedOperationError: rowid is not supported in pandas backends`.

```python
import duckdb
import ibis
import pandas as pd

from pathlib import Path

df = pd.DataFrame(
    {
        "actual": [100, 150, 125, 140, 145, 135, 123],
        "forecast": [90, 160, 125, 150, 141, 141, 120],
        "numerical": [1.1, 1.9, 3.2, 3.8, 4.3, 5.0, 5.5],
        "date": pd.date_range("2022-01-03", "2022-01-09"),
        "string": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
    },
)

table = ibis.pandas.connect({"df": df}).table("df")

table2 = table.mutate(row_id=table.rowid())
table2.execute()
```

I would expect it to work as the documentation states the Pandas backend supports it

![image](https://user-images.githubusercontent.com/42288570/205232073-9ee8d409-28af-4542-9880-d9f67f4d9fdf.png)

### What version of ibis are you using?

3.2.0

### What backend(s) are you using, if any?

Pandas

### Relevant log output

```sh
$ python script.py
Traceback (most recent call last):
  File "C:\repos\private\tmp\holoviews\script.py", line 20, in <module>
    table2.execute()
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\expr\types\core.py", line 291, in execute
    return self._find_backend(use_default=True).execute(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\__init__.py", line 216, in execute
    return execute_and_reset(query, params=params, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 491, in execute_and_reset
    result = execute(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\multipledispatch\dispatcher.py", line 278, in __call__
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\trace.py", line 137, in traced_func
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 436, in main_execute
    return execute_with_scope(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 222, in execute_with_scope
    result = execute_until_in_scope(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\trace.py", line 137, in traced_func
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 363, in execute_until_in_scope
    result = execute_node(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\multipledispatch\dispatcher.py", line 278, in __call__
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\trace.py", line 137, in traced_func
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\execution\selection.py", line 403, in execute_selection_dataframe
    result = build_df_from_projection(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\execution\selection.py", line 363, in build_df_from_projection
    data_pieces = [
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\execution\selection.py", line 364, in <listcomp>
    compute_projection(expr, op, data, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\multipledispatch\dispatcher.py", line 278, in __call__
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\execution\selection.py", line 134, in compute_projection_column_expr
    execute(expr, scope=scope, timecontext=timecontext, **kwargs),
  File "C:\repos\private\hvplot\.venv\lib\site-packages\multipledispatch\dispatcher.py", line 278, in __call__
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\trace.py", line 137, in traced_func
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 436, in main_execute
    return execute_with_scope(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 222, in execute_with_scope
    result = execute_until_in_scope(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\trace.py", line 137, in traced_func
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 331, in execute_until_in_scope
    scopes = [
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 332, in <listcomp>
    execute_until_in_scope(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\trace.py", line 137, in traced_func
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\core.py", line 363, in execute_until_in_scope
    result = execute_node(
  File "C:\repos\private\hvplot\.venv\lib\site-packages\multipledispatch\dispatcher.py", line 278, in __call__
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\trace.py", line 137, in traced_func
    return func(*args, **kwargs)
  File "C:\repos\private\hvplot\.venv\lib\site-packages\ibis\backends\pandas\execution\generic.py", line 1301, in execute_rowid
    raise com.UnsupportedOperationError(
ibis.common.exceptions.UnsupportedOperationError: rowid is not supported in pandas backends
```


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

### Additional Context

The problem I am actually trying to solve is to define a way to provide an `iloc` method to an ibis table. The way HoloViews supports any data source is that you define a specific `Interface` like the `IbisInterface` that provides the methods like `iloc` that are needed for HoloViews to support it.

![image](https://user-images.githubusercontent.com/42288570/205232717-633af2f6-e6a2-4d61-8c0e-80c8291ebaf9.png)

I can see that `DuckDB` that is one of the backends I want to support don't support `rowid`, so I will probably have to find another solution.

(I did not implement the code shown. I'm just trying to get it to work :-))



Discussion:

Resolving patch:
diff --git a/ibis/backends/pandas/dispatch.py b/ibis/backends/pandas/dispatch.py
--- a/ibis/backends/pandas/dispatch.py
+++ b/ibis/backends/pandas/dispatch.py
@@ -19,7 +19,14 @@
 
 
 @execute_node.register(ops.Node)
-def execute_node_without_scope(node, **kwargs):
+def raise_unknown_op(node, **kwargs):
+    raise NotImplementedError(
+        f"Operation {type(node).__name__!r} is not implemented " f"for this backend"
+    )
+
+
+@execute_node.register(ops.TableNode)
+def raise_unknown_table_node(node, **kwargs):
     raise com.UnboundExpressionError(
         (
             'Node of type {!r} has no data bound to it. '
diff --git a/ibis/backends/pandas/execution/generic.py b/ibis/backends/pandas/execution/generic.py
--- a/ibis/backends/pandas/execution/generic.py
+++ b/ibis/backends/pandas/execution/generic.py
@@ -1303,11 +1303,6 @@ def execute_distinct_dataframe(op, df, **kwargs):
     return df.drop_duplicates()
 
 
-@execute_node.register(ops.RowID)
-def execute_rowid(op, *args, **kwargs):
-    raise com.UnsupportedOperationError('rowid is not supported in pandas backends')
-
-
 @execute_node.register(ops.TableArrayView, pd.DataFrame)
 def execute_table_array_view(op, _, **kwargs):
     return execute(op.table).squeeze()


Test patch:
diff --git a/ibis/backends/pandas/tests/test_core.py b/ibis/backends/pandas/tests/test_core.py
--- a/ibis/backends/pandas/tests/test_core.py
+++ b/ibis/backends/pandas/tests/test_core.py
@@ -100,9 +100,7 @@ def table(self, name):
     t = con.table('t')
     with pytest.raises(
         NotImplementedError,
-        match=(
-            'Could not find signature for execute_node: ' '<DatabaseTable, MyBackend>'
-        ),
+        match=('Could not find signature for execute_node: <DatabaseTable, MyBackend>'),
     ):
         con.execute(t)