prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  docs: add blurb about the datafusion backend
  After #2918 lands, we need to mention it in the docs.
  
  </issue>
  <code>
  [start of ibis/backends/datafusion/__init__.py]
  1 from __future__ import annotations
  2 
  3 import re
  4 from typing import Mapping
  5 
  6 import datafusion as df
  7 import pyarrow as pa
  8 
  9 import ibis.common.exceptions as com
  10 import ibis.expr.schema as sch
  11 import ibis.expr.types as ir
  12 from ibis.backends.base import BaseBackend
  13 
  14 from .compiler import translate
  15 
  16 
  17 def _to_pyarrow_table(frame):
  18     batches = frame.collect()
  19     if batches:
  20         return pa.Table.from_batches(batches)
  21     else:
  22         # TODO(kszucs): file a bug to datafusion because the fields'
  23         # nullability from frame.schema() is not always consistent
  24         # with the first record batch's schema
  25         return pa.Table.from_batches(batches, schema=frame.schema())
  26 
  27 
  28 class Backend(BaseBackend):
  29     name = 'datafusion'
  30     builder = None
  31 
  32     @property
  33     def version(self):
  34         try:
  35             import importlib.metadata as importlib_metadata
  36         except ImportError:
  37             # TODO: remove this when Python 3.7 support is dropped
  38             import importlib_metadata
  39         return importlib_metadata.version("datafusion")
  40 
  41     def do_connect(self, config):
  42         """
  43         Create a DataFusionClient for use with Ibis
  44 
  45         Parameters
  46         ----------
  47         config : DataFusionContext or dict
  48 
  49         Returns
  50         -------
  51         DataFusionClient
  52         """
  53         if isinstance(config, df.ExecutionContext):
  54             self._context = config
  55         else:
  56             self._context = df.ExecutionContext()
  57 
  58         for name, path in config.items():
  59             strpath = str(path)
  60             if strpath.endswith('.csv'):
  61                 self.register_csv(name, path)
  62             elif strpath.endswith('.parquet'):
  63                 self.register_parquet(name, path)
  64             else:
  65                 raise ValueError(
  66                     "Currently the DataFusion backend only supports CSV "
  67                     "files with the extension .csv and Parquet files with "
  68                     "the .parquet extension."
  69                 )
  70 
  71     def current_database(self):
  72         raise NotImplementedError()
  73 
  74     def list_databases(self, like: str = None) -> list[str]:
  75         raise NotImplementedError()
  76 
  77     def list_tables(self, like: str = None, database: str = None) -> list[str]:
  78         """List the available tables."""
  79         tables = list(self._context.tables())
  80         if like is not None:
  81             pattern = re.compile(like)
  82             return list(filter(lambda t: pattern.findall(t), tables))
  83         return tables
  84 
  85     def table(self, name, schema=None):
  86         catalog = self._context.catalog()
  87         database = catalog.database('public')
  88         table = database.table(name)
  89         schema = sch.infer(table.schema)
  90         return self.table_class(name, schema, self).to_expr()
  91 
  92     def register_csv(self, name, path, schema=None):
  93         self._context.register_csv(name, path, schema=schema)
  94 
  95     def register_parquet(self, name, path, schema=None):
  96         self._context.register_parquet(name, path, schema=schema)
  97 
  98     def execute(
  99         self,
  100         expr: ir.Expr,
  101         params: Mapping[ir.Expr, object] = None,
  102         limit: str = 'default',
  103         **kwargs,
  104     ):
  105         if isinstance(expr, ir.TableExpr):
  106             frame = self.compile(expr, params, **kwargs)
  107             table = _to_pyarrow_table(frame)
  108             return table.to_pandas()
  109         elif isinstance(expr, ir.ColumnExpr):
  110             # expression must be named for the projection
  111             expr = expr.name('tmp').to_projection()
  112             frame = self.compile(expr, params, **kwargs)
  113             table = _to_pyarrow_table(frame)
  114             return table['tmp'].to_pandas()
  115         elif isinstance(expr, ir.ScalarExpr):
  116             if expr.op().root_tables():
  117                 # there are associated datafusion tables so convert the expr
  118                 # to a selection which we can directly convert to a datafusion
  119                 # plan
  120                 expr = expr.name('tmp').to_projection()
  121                 frame = self.compile(expr, params, **kwargs)
  122             else:
  123                 # doesn't have any tables associated so create a plan from a
  124                 # dummy datafusion table
  125                 compiled = self.compile(expr, params, **kwargs)
  126                 frame = self._context.empty_table().select(compiled)
  127             table = _to_pyarrow_table(frame)
  128             return table[0][0].as_py()
  129         else:
  130             raise com.IbisError(
  131                 f"Cannot execute expression of type: {type(expr)}"
  132             )
  133 
  134     def compile(
  135         self, expr: ir.Expr, params: Mapping[ir.Expr, object] = None, **kwargs
  136     ):
  137         return translate(expr)
  138
  [end of ibis/backends/datafusion/__init__.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/ibis/backends/datafusion/__init__.py b/ibis/backends/datafusion/__init__.py
  --- a/ibis/backends/datafusion/__init__.py
  +++ b/ibis/backends/datafusion/__init__.py
  @@ -83,6 +83,20 @@
           return tables
   
       def table(self, name, schema=None):
  +        """Get an ibis expression representing a DataFusion table.
  +
  +        Parameters
  +        ---------
  +        name
  +            The name of the table to retreive
  +        schema
  +            An optional schema
  +
  +        Returns
  +        -------
  +        ibis.expr.types.TableExpr
  +            A table expression
  +        """
           catalog = self._context.catalog()
           database = catalog.database('public')
           table = database.table(name)
  @@ -90,9 +104,31 @@
           return self.table_class(name, schema, self).to_expr()
   
       def register_csv(self, name, path, schema=None):
  +        """Register a CSV file with with `name` located at `path`.
  +
  +        Parameters
  +        ----------
  +        name
  +            The name of the table
  +        path
  +            The path to the CSV file
  +        schema
  +            An optional schema
  +        """
           self._context.register_csv(name, path, schema=schema)
   
       def register_parquet(self, name, path, schema=None):
  +        """Register a parquet file with with `name` located at `path`.
  +
  +        Parameters
  +        ----------
  +        name
  +            The name of the table
  +        path
  +            The path to the parquet file
  +        schema
  +            An optional schema
  +        """
           self._context.register_parquet(name, path, schema=schema)
   
       def execute(
  
verification_info: {"golden_diff": "diff --git a/ibis/backends/datafusion/__init__.py b/ibis/backends/datafusion/__init__.py\n--- a/ibis/backends/datafusion/__init__.py\n+++ b/ibis/backends/datafusion/__init__.py\n@@ -83,6 +83,20 @@\n         return tables\n \n     def table(self, name, schema=None):\n+        \"\"\"Get an ibis expression representing a DataFusion table.\n+\n+        Parameters\n+        ---------\n+        name\n+            The name of the table to retreive\n+        schema\n+            An optional schema\n+\n+        Returns\n+        -------\n+        ibis.expr.types.TableExpr\n+            A table expression\n+        \"\"\"\n         catalog = self._context.catalog()\n         database = catalog.database('public')\n         table = database.table(name)\n@@ -90,9 +104,31 @@\n         return self.table_class(name, schema, self).to_expr()\n \n     def register_csv(self, name, path, schema=None):\n+        \"\"\"Register a CSV file with with `name` located at `path`.\n+\n+        Parameters\n+        ----------\n+        name\n+            The name of the table\n+        path\n+            The path to the CSV file\n+        schema\n+            An optional schema\n+        \"\"\"\n         self._context.register_csv(name, path, schema=schema)\n \n     def register_parquet(self, name, path, schema=None):\n+        \"\"\"Register a parquet file with with `name` located at `path`.\n+\n+        Parameters\n+        ----------\n+        name\n+            The name of the table\n+        path\n+            The path to the parquet file\n+        schema\n+            An optional schema\n+        \"\"\"\n         self._context.register_parquet(name, path, schema=schema)\n \n     def execute(\n", "issue": "docs: add blurb about the datafusion backend\nAfter #2918 lands, we need to mention it in the docs.\n", "before_files": [{"content": "from __future__ import annotations\n\nimport re\nfrom typing import Mapping\n\nimport datafusion as df\nimport pyarrow as pa\n\nimport ibis.common.exceptions as com\nimport ibis.expr.schema as sch\nimport ibis.expr.types as ir\nfrom ibis.backends.base import BaseBackend\n\nfrom .compiler import translate\n\n\ndef _to_pyarrow_table(frame):\n    batches = frame.collect()\n    if batches:\n        return pa.Table.from_batches(batches)\n    else:\n        # TODO(kszucs): file a bug to datafusion because the fields'\n        # nullability from frame.schema() is not always consistent\n        # with the first record batch's schema\n        return pa.Table.from_batches(batches, schema=frame.schema())\n\n\nclass Backend(BaseBackend):\n    name = 'datafusion'\n    builder = None\n\n    @property\n    def version(self):\n        try:\n            import importlib.metadata as importlib_metadata\n        except ImportError:\n            # TODO: remove this when Python 3.7 support is dropped\n            import importlib_metadata\n        return importlib_metadata.version(\"datafusion\")\n\n    def do_connect(self, config):\n        \"\"\"\n        Create a DataFusionClient for use with Ibis\n\n        Parameters\n        ----------\n        config : DataFusionContext or dict\n\n        Returns\n        -------\n        DataFusionClient\n        \"\"\"\n        if isinstance(config, df.ExecutionContext):\n            self._context = config\n        else:\n            self._context = df.ExecutionContext()\n\n        for name, path in config.items():\n            strpath = str(path)\n            if strpath.endswith('.csv'):\n                self.register_csv(name, path)\n            elif strpath.endswith('.parquet'):\n                self.register_parquet(name, path)\n            else:\n                raise ValueError(\n                    \"Currently the DataFusion backend only supports CSV \"\n                    \"files with the extension .csv and Parquet files with \"\n                    \"the .parquet extension.\"\n                )\n\n    def current_database(self):\n        raise NotImplementedError()\n\n    def list_databases(self, like: str = None) -> list[str]:\n        raise NotImplementedError()\n\n    def list_tables(self, like: str = None, database: str = None) -> list[str]:\n        \"\"\"List the available tables.\"\"\"\n        tables = list(self._context.tables())\n        if like is not None:\n            pattern = re.compile(like)\n            return list(filter(lambda t: pattern.findall(t), tables))\n        return tables\n\n    def table(self, name, schema=None):\n        catalog = self._context.catalog()\n        database = catalog.database('public')\n        table = database.table(name)\n        schema = sch.infer(table.schema)\n        return self.table_class(name, schema, self).to_expr()\n\n    def register_csv(self, name, path, schema=None):\n        self._context.register_csv(name, path, schema=schema)\n\n    def register_parquet(self, name, path, schema=None):\n        self._context.register_parquet(name, path, schema=schema)\n\n    def execute(\n        self,\n        expr: ir.Expr,\n        params: Mapping[ir.Expr, object] = None,\n        limit: str = 'default',\n        **kwargs,\n    ):\n        if isinstance(expr, ir.TableExpr):\n            frame = self.compile(expr, params, **kwargs)\n            table = _to_pyarrow_table(frame)\n            return table.to_pandas()\n        elif isinstance(expr, ir.ColumnExpr):\n            # expression must be named for the projection\n            expr = expr.name('tmp').to_projection()\n            frame = self.compile(expr, params, **kwargs)\n            table = _to_pyarrow_table(frame)\n            return table['tmp'].to_pandas()\n        elif isinstance(expr, ir.ScalarExpr):\n            if expr.op().root_tables():\n                # there are associated datafusion tables so convert the expr\n                # to a selection which we can directly convert to a datafusion\n                # plan\n                expr = expr.name('tmp').to_projection()\n                frame = self.compile(expr, params, **kwargs)\n            else:\n                # doesn't have any tables associated so create a plan from a\n                # dummy datafusion table\n                compiled = self.compile(expr, params, **kwargs)\n                frame = self._context.empty_table().select(compiled)\n            table = _to_pyarrow_table(frame)\n            return table[0][0].as_py()\n        else:\n            raise com.IbisError(\n                f\"Cannot execute expression of type: {type(expr)}\"\n            )\n\n    def compile(\n        self, expr: ir.Expr, params: Mapping[ir.Expr, object] = None, **kwargs\n    ):\n        return translate(expr)\n", "path": "ibis/backends/datafusion/__init__.py"}]}