We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement `columns.delete` RPC method
Replaces `DELETE /api/db/v0/tables/{tableId}/columns/{columnId}`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `db/sql/install.py`
Content:
```
1 import os
2 from db.connection import load_file_with_engine
3 
4 FILE_DIR = os.path.abspath(os.path.dirname(__file__))
5 MSAR_SQL = os.path.join(FILE_DIR, '0_msar.sql')
6 MSAR_AGGREGATE_SQL = os.path.join(FILE_DIR, '3_msar_custom_aggregates.sql')
7 
8 
9 def install(engine):
10     """Install SQL pieces using the given engine."""
11     with open(MSAR_SQL) as file_handle:
12         load_file_with_engine(engine, file_handle)
13     with open(MSAR_AGGREGATE_SQL) as custom_aggregates:
14         load_file_with_engine(engine, custom_aggregates)
15
```

Path: `mathesar/rpc/columns.py`
Content:
```
1 """
2 Classes and functions exposed to the RPC endpoint for managing table columns.
3 """
4 from typing import TypedDict
5 
6 from modernrpc.core import rpc_method, REQUEST_KEY
7 from modernrpc.auth.basic import http_basic_auth_login_required
8 
9 from db.columns.operations.select import get_column_info_for_table
10 from mathesar.rpc.exceptions.handlers import handle_rpc_exceptions
11 from mathesar.rpc.utils import connect
12 from mathesar.utils.columns import get_raw_display_options
13 
14 
15 class TypeOptions(TypedDict, total=False):
16     """
17     Options applied to a type. All attributes are optional.
18 
19     Take special care with the difference between numeric and date/time
20     types w.r.t. precision. The attribute has a different meaning
21     depending on the type to which it's being applied.
22 
23     Attributes:
24         precision: For numeric types, the number of significant digits.
25                    For date/time types, the number of fractional digits.
26         scale: For numeric types, the number of fractional digits.
27         fields: Which time fields are stored. See Postgres docs.
28         length: The maximum length of a character-type field.
29         item_type: The member type for arrays.
30     """
31     precision: int
32     scale: int
33     fields: str
34     length: int
35     item_type: str
36 
37     @classmethod
38     def from_dict(cls, type_options):
39         if type_options is None:
40             return
41         # All keys are optional, but we want to validate the keys we
42         # actually return.
43         all_keys = dict(
44             precision=type_options.get("precision"),
45             scale=type_options.get("scale"),
46             fields=type_options.get("fields"),
47             length=type_options.get("length"),
48             item_type=type_options.get("item_type"),
49         )
50         reduced_keys = {k: v for k, v in all_keys.items() if v is not None}
51         if reduced_keys != {}:
52             return cls(**reduced_keys)
53 
54 
55 class ColumnDefault(TypedDict):
56     """
57     A dictionary describing the default value for a column.
58 
59     Attributes:
60         value: An SQL expression giving the default value.
61         is_dynamic: Whether the `value` is possibly dynamic.
62     """
63     value: str
64     is_dynamic: bool
65 
66     @classmethod
67     def from_dict(cls, col_default):
68         if col_default is not None:
69             return cls(
70                 value=col_default["value"],
71                 is_dynamic=col_default["is_dynamic"],
72             )
73 
74 
75 class ColumnInfo(TypedDict):
76     """
77     Information about a column.
78 
79     Attributes:
80         id: The `attnum` of the column in the table.
81         name: The name of the column.
82         type: The type of the column on the database.
83         type_options: The options applied to the column type.
84         nullable: Whether or not the column is nullable.
85         primary_key: Whether the column is in the primary key.
86         default: The default value and whether it's dynamic.
87         has_dependents: Whether the column has dependent objects.
88         description: The description of the column.
89     """
90     id: int
91     name: str
92     type: str
93     type_options: TypeOptions
94     nullable: bool
95     primary_key: bool
96     default: ColumnDefault
97     has_dependents: bool
98     description: str
99 
100     @classmethod
101     def from_dict(cls, col_info):
102         return cls(
103             id=col_info["id"],
104             name=col_info["name"],
105             type=col_info["type"],
106             type_options=TypeOptions.from_dict(col_info.get("type_options")),
107             nullable=col_info["nullable"],
108             primary_key=col_info["primary_key"],
109             default=ColumnDefault.from_dict(col_info.get("default")),
110             has_dependents=col_info["has_dependents"],
111             description=col_info.get("description")
112         )
113 
114 
115 class ColumnListReturn(TypedDict):
116     """
117     Information about the columns of a table.
118 
119     Attributes:
120         column_info: Column information from the user's database.
121         display_options: Display metadata managed by Mathesar.
122     """
123     column_info: list[ColumnInfo]
124     display_options: list[dict]
125 
126 
127 @rpc_method(name="columns.list")
128 @http_basic_auth_login_required
129 @handle_rpc_exceptions
130 def list_(*, table_oid: int, database_id: int, **kwargs) -> ColumnListReturn:
131     """
132     List information about columns for a table. Exposed as `list`.
133 
134     Also return display options for each column, if they're defined.
135 
136     Args:
137         table_oid: Identity of the table in the user's database.
138         database_id: The Django id of the database containing the table.
139 
140     Returns:
141         A list of column details, and a separate list of display options.
142     """
143     user = kwargs.get(REQUEST_KEY).user
144     with connect(database_id, user) as conn:
145         raw_column_info = get_column_info_for_table(table_oid, conn)
146     column_info, attnums = tuple(
147         zip(
148             *[(ColumnInfo.from_dict(col), col['id']) for col in raw_column_info]
149         )
150     )
151     display_options = get_raw_display_options(
152         database_id, table_oid, attnums, user
153     )
154     return ColumnListReturn(
155         column_info=column_info,
156         display_options=display_options,
157     )
158
```

Path: `db/columns/operations/drop.py`
Content:
```
1 """The function in this module wraps SQL functions that drop columns."""
2 from db import connection as db_conn
3 
4 
5 def drop_column(table_oid, column_attnum, engine):
6     """
7     Drop the given columns from the given table.
8 
9     Args:
10         table_oid: OID of the table whose columns we'll drop.
11         column_attnum: The attnums of the columns to drop.
12         engine: SQLAlchemy engine object for connecting.
13 
14     Returns:
15         Returns a string giving the command that was run.
16     """
17     return db_conn.execute_msar_func_with_engine(
18         engine, 'drop_columns', table_oid, column_attnum
19     ).fetchone()[0]
20
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/db/columns/operations/drop.py b/db/columns/operations/drop.py
--- a/db/columns/operations/drop.py
+++ b/db/columns/operations/drop.py
@@ -1,14 +1,14 @@
-"""The function in this module wraps SQL functions that drop columns."""
+"""The functions in this module wrap SQL functions that drop columns."""
 from db import connection as db_conn
 
 
 def drop_column(table_oid, column_attnum, engine):
     """
-    Drop the given columns from the given table.
+    Drop the given column from the given table.
 
     Args:
-        table_oid: OID of the table whose columns we'll drop.
-        column_attnum: The attnums of the columns to drop.
+        table_oid: OID of the table whose column we'll drop.
+        column_attnum: The attnum of the column to drop.
         engine: SQLAlchemy engine object for connecting.
 
     Returns:
@@ -17,3 +17,17 @@
     return db_conn.execute_msar_func_with_engine(
         engine, 'drop_columns', table_oid, column_attnum
     ).fetchone()[0]
+
+
+def drop_columns_from_table(table_oid, column_attnums, conn):
+    """
+    Drop the given columns from the given table.
+
+    Args:
+        table_oid: OID of the table whose columns we'll drop.
+        column_attnums: The attnums of the columns to drop.
+        conn: A psycopg connection to the relevant database.
+    """
+    return db_conn.exec_msar_func(
+        conn, 'drop_columns', table_oid, *column_attnums
+    ).fetchone()[0]
diff --git a/db/sql/install.py b/db/sql/install.py
--- a/db/sql/install.py
+++ b/db/sql/install.py
@@ -2,8 +2,8 @@
 from db.connection import load_file_with_engine
 
 FILE_DIR = os.path.abspath(os.path.dirname(__file__))
-MSAR_SQL = os.path.join(FILE_DIR, '0_msar.sql')
-MSAR_AGGREGATE_SQL = os.path.join(FILE_DIR, '3_msar_custom_aggregates.sql')
+MSAR_SQL = os.path.join(FILE_DIR, '00_msar.sql')
+MSAR_AGGREGATE_SQL = os.path.join(FILE_DIR, '30_msar_custom_aggregates.sql')
 
 
 def install(engine):
diff --git a/mathesar/rpc/columns.py b/mathesar/rpc/columns.py
--- a/mathesar/rpc/columns.py
+++ b/mathesar/rpc/columns.py
@@ -7,6 +7,7 @@
 from modernrpc.auth.basic import http_basic_auth_login_required
 
 from db.columns.operations.select import get_column_info_for_table
+from db.columns.operations.drop import drop_columns_from_table
 from mathesar.rpc.exceptions.handlers import handle_rpc_exceptions
 from mathesar.rpc.utils import connect
 from mathesar.utils.columns import get_raw_display_options
@@ -155,3 +156,25 @@
         column_info=column_info,
         display_options=display_options,
     )
+
+
+@rpc_method(name="columns.delete")
+@http_basic_auth_login_required
+@handle_rpc_exceptions
+def delete(
+        *, column_attnums: list[int], table_oid: int, database_id: int, **kwargs
+) -> int:
+    """
+    Delete columns from a table.
+
+    Args:
+        column_attnums: A list of attnums of columns to delete.
+        table_oid: Identity of the table in the user's database.
+        database_id: The Django id of the database containing the table.
+
+    Returns:
+        The number of columns dropped.
+    """
+    user = kwargs.get(REQUEST_KEY).user
+    with connect(database_id, user) as conn:
+        return drop_columns_from_table(table_oid, column_attnums, conn)

{"golden_diff": "diff --git a/db/columns/operations/drop.py b/db/columns/operations/drop.py\n--- a/db/columns/operations/drop.py\n+++ b/db/columns/operations/drop.py\n@@ -1,14 +1,14 @@\n-\"\"\"The function in this module wraps SQL functions that drop columns.\"\"\"\n+\"\"\"The functions in this module wrap SQL functions that drop columns.\"\"\"\n from db import connection as db_conn\n \n \n def drop_column(table_oid, column_attnum, engine):\n     \"\"\"\n-    Drop the given columns from the given table.\n+    Drop the given column from the given table.\n \n     Args:\n-        table_oid: OID of the table whose columns we'll drop.\n-        column_attnum: The attnums of the columns to drop.\n+        table_oid: OID of the table whose column we'll drop.\n+        column_attnum: The attnum of the column to drop.\n         engine: SQLAlchemy engine object for connecting.\n \n     Returns:\n@@ -17,3 +17,17 @@\n     return db_conn.execute_msar_func_with_engine(\n         engine, 'drop_columns', table_oid, column_attnum\n     ).fetchone()[0]\n+\n+\n+def drop_columns_from_table(table_oid, column_attnums, conn):\n+    \"\"\"\n+    Drop the given columns from the given table.\n+\n+    Args:\n+        table_oid: OID of the table whose columns we'll drop.\n+        column_attnums: The attnums of the columns to drop.\n+        conn: A psycopg connection to the relevant database.\n+    \"\"\"\n+    return db_conn.exec_msar_func(\n+        conn, 'drop_columns', table_oid, *column_attnums\n+    ).fetchone()[0]\ndiff --git a/db/sql/install.py b/db/sql/install.py\n--- a/db/sql/install.py\n+++ b/db/sql/install.py\n@@ -2,8 +2,8 @@\n from db.connection import load_file_with_engine\n \n FILE_DIR = os.path.abspath(os.path.dirname(__file__))\n-MSAR_SQL = os.path.join(FILE_DIR, '0_msar.sql')\n-MSAR_AGGREGATE_SQL = os.path.join(FILE_DIR, '3_msar_custom_aggregates.sql')\n+MSAR_SQL = os.path.join(FILE_DIR, '00_msar.sql')\n+MSAR_AGGREGATE_SQL = os.path.join(FILE_DIR, '30_msar_custom_aggregates.sql')\n \n \n def install(engine):\ndiff --git a/mathesar/rpc/columns.py b/mathesar/rpc/columns.py\n--- a/mathesar/rpc/columns.py\n+++ b/mathesar/rpc/columns.py\n@@ -7,6 +7,7 @@\n from modernrpc.auth.basic import http_basic_auth_login_required\n \n from db.columns.operations.select import get_column_info_for_table\n+from db.columns.operations.drop import drop_columns_from_table\n from mathesar.rpc.exceptions.handlers import handle_rpc_exceptions\n from mathesar.rpc.utils import connect\n from mathesar.utils.columns import get_raw_display_options\n@@ -155,3 +156,25 @@\n         column_info=column_info,\n         display_options=display_options,\n     )\n+\n+\n+@rpc_method(name=\"columns.delete\")\n+@http_basic_auth_login_required\n+@handle_rpc_exceptions\n+def delete(\n+        *, column_attnums: list[int], table_oid: int, database_id: int, **kwargs\n+) -> int:\n+    \"\"\"\n+    Delete columns from a table.\n+\n+    Args:\n+        column_attnums: A list of attnums of columns to delete.\n+        table_oid: Identity of the table in the user's database.\n+        database_id: The Django id of the database containing the table.\n+\n+    Returns:\n+        The number of columns dropped.\n+    \"\"\"\n+    user = kwargs.get(REQUEST_KEY).user\n+    with connect(database_id, user) as conn:\n+        return drop_columns_from_table(table_oid, column_attnums, conn)\n", "issue": "Implement `columns.delete` RPC method\nReplaces `DELETE /api/db/v0/tables/{tableId}/columns/{columnId}`\n", "before_files": [{"content": "import os\nfrom db.connection import load_file_with_engine\n\nFILE_DIR = os.path.abspath(os.path.dirname(__file__))\nMSAR_SQL = os.path.join(FILE_DIR, '0_msar.sql')\nMSAR_AGGREGATE_SQL = os.path.join(FILE_DIR, '3_msar_custom_aggregates.sql')\n\n\ndef install(engine):\n    \"\"\"Install SQL pieces using the given engine.\"\"\"\n    with open(MSAR_SQL) as file_handle:\n        load_file_with_engine(engine, file_handle)\n    with open(MSAR_AGGREGATE_SQL) as custom_aggregates:\n        load_file_with_engine(engine, custom_aggregates)\n", "path": "db/sql/install.py"}, {"content": "\"\"\"\nClasses and functions exposed to the RPC endpoint for managing table columns.\n\"\"\"\nfrom typing import TypedDict\n\nfrom modernrpc.core import rpc_method, REQUEST_KEY\nfrom modernrpc.auth.basic import http_basic_auth_login_required\n\nfrom db.columns.operations.select import get_column_info_for_table\nfrom mathesar.rpc.exceptions.handlers import handle_rpc_exceptions\nfrom mathesar.rpc.utils import connect\nfrom mathesar.utils.columns import get_raw_display_options\n\n\nclass TypeOptions(TypedDict, total=False):\n    \"\"\"\n    Options applied to a type. All attributes are optional.\n\n    Take special care with the difference between numeric and date/time\n    types w.r.t. precision. The attribute has a different meaning\n    depending on the type to which it's being applied.\n\n    Attributes:\n        precision: For numeric types, the number of significant digits.\n                   For date/time types, the number of fractional digits.\n        scale: For numeric types, the number of fractional digits.\n        fields: Which time fields are stored. See Postgres docs.\n        length: The maximum length of a character-type field.\n        item_type: The member type for arrays.\n    \"\"\"\n    precision: int\n    scale: int\n    fields: str\n    length: int\n    item_type: str\n\n    @classmethod\n    def from_dict(cls, type_options):\n        if type_options is None:\n            return\n        # All keys are optional, but we want to validate the keys we\n        # actually return.\n        all_keys = dict(\n            precision=type_options.get(\"precision\"),\n            scale=type_options.get(\"scale\"),\n            fields=type_options.get(\"fields\"),\n            length=type_options.get(\"length\"),\n            item_type=type_options.get(\"item_type\"),\n        )\n        reduced_keys = {k: v for k, v in all_keys.items() if v is not None}\n        if reduced_keys != {}:\n            return cls(**reduced_keys)\n\n\nclass ColumnDefault(TypedDict):\n    \"\"\"\n    A dictionary describing the default value for a column.\n\n    Attributes:\n        value: An SQL expression giving the default value.\n        is_dynamic: Whether the `value` is possibly dynamic.\n    \"\"\"\n    value: str\n    is_dynamic: bool\n\n    @classmethod\n    def from_dict(cls, col_default):\n        if col_default is not None:\n            return cls(\n                value=col_default[\"value\"],\n                is_dynamic=col_default[\"is_dynamic\"],\n            )\n\n\nclass ColumnInfo(TypedDict):\n    \"\"\"\n    Information about a column.\n\n    Attributes:\n        id: The `attnum` of the column in the table.\n        name: The name of the column.\n        type: The type of the column on the database.\n        type_options: The options applied to the column type.\n        nullable: Whether or not the column is nullable.\n        primary_key: Whether the column is in the primary key.\n        default: The default value and whether it's dynamic.\n        has_dependents: Whether the column has dependent objects.\n        description: The description of the column.\n    \"\"\"\n    id: int\n    name: str\n    type: str\n    type_options: TypeOptions\n    nullable: bool\n    primary_key: bool\n    default: ColumnDefault\n    has_dependents: bool\n    description: str\n\n    @classmethod\n    def from_dict(cls, col_info):\n        return cls(\n            id=col_info[\"id\"],\n            name=col_info[\"name\"],\n            type=col_info[\"type\"],\n            type_options=TypeOptions.from_dict(col_info.get(\"type_options\")),\n            nullable=col_info[\"nullable\"],\n            primary_key=col_info[\"primary_key\"],\n            default=ColumnDefault.from_dict(col_info.get(\"default\")),\n            has_dependents=col_info[\"has_dependents\"],\n            description=col_info.get(\"description\")\n        )\n\n\nclass ColumnListReturn(TypedDict):\n    \"\"\"\n    Information about the columns of a table.\n\n    Attributes:\n        column_info: Column information from the user's database.\n        display_options: Display metadata managed by Mathesar.\n    \"\"\"\n    column_info: list[ColumnInfo]\n    display_options: list[dict]\n\n\n@rpc_method(name=\"columns.list\")\n@http_basic_auth_login_required\n@handle_rpc_exceptions\ndef list_(*, table_oid: int, database_id: int, **kwargs) -> ColumnListReturn:\n    \"\"\"\n    List information about columns for a table. Exposed as `list`.\n\n    Also return display options for each column, if they're defined.\n\n    Args:\n        table_oid: Identity of the table in the user's database.\n        database_id: The Django id of the database containing the table.\n\n    Returns:\n        A list of column details, and a separate list of display options.\n    \"\"\"\n    user = kwargs.get(REQUEST_KEY).user\n    with connect(database_id, user) as conn:\n        raw_column_info = get_column_info_for_table(table_oid, conn)\n    column_info, attnums = tuple(\n        zip(\n            *[(ColumnInfo.from_dict(col), col['id']) for col in raw_column_info]\n        )\n    )\n    display_options = get_raw_display_options(\n        database_id, table_oid, attnums, user\n    )\n    return ColumnListReturn(\n        column_info=column_info,\n        display_options=display_options,\n    )\n", "path": "mathesar/rpc/columns.py"}, {"content": "\"\"\"The function in this module wraps SQL functions that drop columns.\"\"\"\nfrom db import connection as db_conn\n\n\ndef drop_column(table_oid, column_attnum, engine):\n    \"\"\"\n    Drop the given columns from the given table.\n\n    Args:\n        table_oid: OID of the table whose columns we'll drop.\n        column_attnum: The attnums of the columns to drop.\n        engine: SQLAlchemy engine object for connecting.\n\n    Returns:\n        Returns a string giving the command that was run.\n    \"\"\"\n    return db_conn.execute_msar_func_with_engine(\n        engine, 'drop_columns', table_oid, column_attnum\n    ).fetchone()[0]\n", "path": "db/columns/operations/drop.py"}], "after_files": [{"content": "import os\nfrom db.connection import load_file_with_engine\n\nFILE_DIR = os.path.abspath(os.path.dirname(__file__))\nMSAR_SQL = os.path.join(FILE_DIR, '00_msar.sql')\nMSAR_AGGREGATE_SQL = os.path.join(FILE_DIR, '30_msar_custom_aggregates.sql')\n\n\ndef install(engine):\n    \"\"\"Install SQL pieces using the given engine.\"\"\"\n    with open(MSAR_SQL) as file_handle:\n        load_file_with_engine(engine, file_handle)\n    with open(MSAR_AGGREGATE_SQL) as custom_aggregates:\n        load_file_with_engine(engine, custom_aggregates)\n", "path": "db/sql/install.py"}, {"content": "\"\"\"\nClasses and functions exposed to the RPC endpoint for managing table columns.\n\"\"\"\nfrom typing import TypedDict\n\nfrom modernrpc.core import rpc_method, REQUEST_KEY\nfrom modernrpc.auth.basic import http_basic_auth_login_required\n\nfrom db.columns.operations.select import get_column_info_for_table\nfrom db.columns.operations.drop import drop_columns_from_table\nfrom mathesar.rpc.exceptions.handlers import handle_rpc_exceptions\nfrom mathesar.rpc.utils import connect\nfrom mathesar.utils.columns import get_raw_display_options\n\n\nclass TypeOptions(TypedDict, total=False):\n    \"\"\"\n    Options applied to a type. All attributes are optional.\n\n    Take special care with the difference between numeric and date/time\n    types w.r.t. precision. The attribute has a different meaning\n    depending on the type to which it's being applied.\n\n    Attributes:\n        precision: For numeric types, the number of significant digits.\n                   For date/time types, the number of fractional digits.\n        scale: For numeric types, the number of fractional digits.\n        fields: Which time fields are stored. See Postgres docs.\n        length: The maximum length of a character-type field.\n        item_type: The member type for arrays.\n    \"\"\"\n    precision: int\n    scale: int\n    fields: str\n    length: int\n    item_type: str\n\n    @classmethod\n    def from_dict(cls, type_options):\n        if type_options is None:\n            return\n        # All keys are optional, but we want to validate the keys we\n        # actually return.\n        all_keys = dict(\n            precision=type_options.get(\"precision\"),\n            scale=type_options.get(\"scale\"),\n            fields=type_options.get(\"fields\"),\n            length=type_options.get(\"length\"),\n            item_type=type_options.get(\"item_type\"),\n        )\n        reduced_keys = {k: v for k, v in all_keys.items() if v is not None}\n        if reduced_keys != {}:\n            return cls(**reduced_keys)\n\n\nclass ColumnDefault(TypedDict):\n    \"\"\"\n    A dictionary describing the default value for a column.\n\n    Attributes:\n        value: An SQL expression giving the default value.\n        is_dynamic: Whether the `value` is possibly dynamic.\n    \"\"\"\n    value: str\n    is_dynamic: bool\n\n    @classmethod\n    def from_dict(cls, col_default):\n        if col_default is not None:\n            return cls(\n                value=col_default[\"value\"],\n                is_dynamic=col_default[\"is_dynamic\"],\n            )\n\n\nclass ColumnInfo(TypedDict):\n    \"\"\"\n    Information about a column.\n\n    Attributes:\n        id: The `attnum` of the column in the table.\n        name: The name of the column.\n        type: The type of the column on the database.\n        type_options: The options applied to the column type.\n        nullable: Whether or not the column is nullable.\n        primary_key: Whether the column is in the primary key.\n        default: The default value and whether it's dynamic.\n        has_dependents: Whether the column has dependent objects.\n        description: The description of the column.\n    \"\"\"\n    id: int\n    name: str\n    type: str\n    type_options: TypeOptions\n    nullable: bool\n    primary_key: bool\n    default: ColumnDefault\n    has_dependents: bool\n    description: str\n\n    @classmethod\n    def from_dict(cls, col_info):\n        return cls(\n            id=col_info[\"id\"],\n            name=col_info[\"name\"],\n            type=col_info[\"type\"],\n            type_options=TypeOptions.from_dict(col_info.get(\"type_options\")),\n            nullable=col_info[\"nullable\"],\n            primary_key=col_info[\"primary_key\"],\n            default=ColumnDefault.from_dict(col_info.get(\"default\")),\n            has_dependents=col_info[\"has_dependents\"],\n            description=col_info.get(\"description\")\n        )\n\n\nclass ColumnListReturn(TypedDict):\n    \"\"\"\n    Information about the columns of a table.\n\n    Attributes:\n        column_info: Column information from the user's database.\n        display_options: Display metadata managed by Mathesar.\n    \"\"\"\n    column_info: list[ColumnInfo]\n    display_options: list[dict]\n\n\n@rpc_method(name=\"columns.list\")\n@http_basic_auth_login_required\n@handle_rpc_exceptions\ndef list_(*, table_oid: int, database_id: int, **kwargs) -> ColumnListReturn:\n    \"\"\"\n    List information about columns for a table. Exposed as `list`.\n\n    Also return display options for each column, if they're defined.\n\n    Args:\n        table_oid: Identity of the table in the user's database.\n        database_id: The Django id of the database containing the table.\n\n    Returns:\n        A list of column details, and a separate list of display options.\n    \"\"\"\n    user = kwargs.get(REQUEST_KEY).user\n    with connect(database_id, user) as conn:\n        raw_column_info = get_column_info_for_table(table_oid, conn)\n    column_info, attnums = tuple(\n        zip(\n            *[(ColumnInfo.from_dict(col), col['id']) for col in raw_column_info]\n        )\n    )\n    display_options = get_raw_display_options(\n        database_id, table_oid, attnums, user\n    )\n    return ColumnListReturn(\n        column_info=column_info,\n        display_options=display_options,\n    )\n\n\n@rpc_method(name=\"columns.delete\")\n@http_basic_auth_login_required\n@handle_rpc_exceptions\ndef delete(\n        *, column_attnums: list[int], table_oid: int, database_id: int, **kwargs\n) -> int:\n    \"\"\"\n    Delete columns from a table.\n\n    Args:\n        column_attnums: A list of attnums of columns to delete.\n        table_oid: Identity of the table in the user's database.\n        database_id: The Django id of the database containing the table.\n\n    Returns:\n        The number of columns dropped.\n    \"\"\"\n    user = kwargs.get(REQUEST_KEY).user\n    with connect(database_id, user) as conn:\n        return drop_columns_from_table(table_oid, column_attnums, conn)\n", "path": "mathesar/rpc/columns.py"}, {"content": "\"\"\"The functions in this module wrap SQL functions that drop columns.\"\"\"\nfrom db import connection as db_conn\n\n\ndef drop_column(table_oid, column_attnum, engine):\n    \"\"\"\n    Drop the given column from the given table.\n\n    Args:\n        table_oid: OID of the table whose column we'll drop.\n        column_attnum: The attnum of the column to drop.\n        engine: SQLAlchemy engine object for connecting.\n\n    Returns:\n        Returns a string giving the command that was run.\n    \"\"\"\n    return db_conn.execute_msar_func_with_engine(\n        engine, 'drop_columns', table_oid, column_attnum\n    ).fetchone()[0]\n\n\ndef drop_columns_from_table(table_oid, column_attnums, conn):\n    \"\"\"\n    Drop the given columns from the given table.\n\n    Args:\n        table_oid: OID of the table whose columns we'll drop.\n        column_attnums: The attnums of the columns to drop.\n        conn: A psycopg connection to the relevant database.\n    \"\"\"\n    return db_conn.exec_msar_func(\n        conn, 'drop_columns', table_oid, *column_attnums\n    ).fetchone()[0]\n", "path": "db/columns/operations/drop.py"}]}