We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement filtering options for date & time types
## Problem
- We need to ensure that records that include columns of all Date & Time types support the following filters via API: 
  - between {x} and {y}
  - is {x}
  - is not {x}
  - before {x}
  - after {x}
  - on or before {x}
  - or or after {x}
  - is empty
  - is not empty
- We need to also ensure that values of the filters don't have to be an exact date. We should accept natural language like "next month", or "tomorrow".
  - We could use https://dateparser.readthedocs.io/

This involves:
- Implementing the filters in the backend
- Updating the `/api/v0/databases/<id>/types/` endpoint to store available filters on this type
  - Filter information should include the number of parameters needing to be passed in (e.g. `between` needs 2 parameters, `is empty` needs 0)

## Additional context
- We're using our fork of `sqlalchemy-filters` to provide filtering. See: https://github.com/centerofci/sqlalchemy-filters.
- This issue is blocked by implementation of Date & Time types: 
  - #424 
  - #425 
  - #426
- #557 provides some context on storing filters.
Implement filtering options for duration type
## Problem
- We need to ensure that records that include columns of duration types support the following filters via API: 
  - between {x} and {y}
  - equals {x}
  - does not equal {x}
  - greater than {x}
  - less than {x}
  - greater than or equals {x}
  - less than or equals  {x}
  - is empty
  - is not empty
- We need to also ensure that values of the filters don't have to be a number. We should accept natural language like "an hour", or "2 days".
  - We could use something like https://github.com/oleiade/durations or https://github.com/wroberts/pytimeparse

This involves:
- Implementing the filters in the backend
- Updating the `/api/v0/databases/<id>/types/` endpoint to store available filters on this type
  - Filter information should include the number of parameters needing to be passed in (e.g. `between` needs 2 parameters, `is empty` needs 0)

## Additional context
- These filters are the same as those for Number types. We should just reuse those.
  - The only additional thing we need to implement is converting natural language to numbers.
  - Relevant issue: #385 
- We're using our fork of `sqlalchemy-filters` to provide filtering. See: https://github.com/centerofci/sqlalchemy-filters.
- This issue is blocked by implementation of #430  
- #557 provides some context on storing filters.

Marking as blocked until #385 and #430 are complete.

--- 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/types/base.py`
Content:
```
1 from enum import Enum
2 
3 from sqlalchemy import create_engine
4 
5 from db import constants
6 
7 from db.functions import hints
8 
9 from frozendict import frozendict
10 
11 
12 CHAR = 'char'
13 STRING = 'string'
14 VARCHAR = 'varchar'
15 
16 
17 class PostgresType(Enum):
18     """
19     This only includes built-in Postgres types that SQLAlchemy supports.
20     SQLAlchemy doesn't support XML. See zzzeek's comment on:
21     https://stackoverflow.com/questions/16153512/using-postgresql-xml-data-type-with-sqlalchemy
22     The values are keys returned by get_available_types.
23     """
24     _ARRAY = '_array'
25     BIGINT = 'bigint'
26     BIT_VARYING = 'bit varying'
27     BIT = 'bit'
28     BOOLEAN = 'boolean'
29     BYTEA = 'bytea'
30     CHAR = '"char"'
31     CHARACTER_VARYING = 'character varying'
32     CHARACTER = 'character'
33     CIDR = 'cidr'
34     DATE = 'date'
35     DATERANGE = 'daterange'
36     DECIMAL = 'decimal'
37     DOUBLE_PRECISION = 'double precision'
38     FLOAT = 'float'
39     HSTORE = 'hstore'
40     INET = 'inet'
41     INT4RANGE = 'int4range'
42     INT8RANGE = 'int8range'
43     INTEGER = 'integer'
44     INTERVAL = 'interval'
45     JSON = 'json'
46     JSONB = 'jsonb'
47     MACADDR = 'macaddr'
48     MONEY = 'money'
49     NAME = 'name'
50     NUMERIC = 'numeric'
51     NUMRANGE = 'numrange'
52     OID = 'oid'
53     REAL = 'real'
54     REGCLASS = 'regclass'
55     SMALLINT = 'smallint'
56     TEXT = 'text'
57     TIME = 'time'
58     TIME_WITH_TIME_ZONE = 'time with time zone'
59     TIME_WITHOUT_TIME_ZONE = 'time without time zone'
60     TIMESTAMP = 'timestamp'
61     TIMESTAMP_WITH_TIME_ZONE = 'timestamp with time zone'
62     TIMESTAMP_WITHOUT_TIME_ZONE = 'timestamp without time zone'
63     TSRANGE = 'tsrange'
64     TSTZRANGE = 'tstzrange'
65     TSVECTOR = 'tsvector'
66     UUID = 'uuid'
67 
68 
69 class MathesarCustomType(Enum):
70     """
71     This is a list of custom Mathesar DB types.
72     Keys returned by get_available_types are of the format 'mathesar_types.VALUE'
73     """
74     EMAIL = 'email'
75     URI = 'uri'
76     MATHESAR_MONEY = 'mathesar_money'
77 
78 
79 _known_vanilla_db_types = tuple(postgres_type for postgres_type in PostgresType)
80 
81 
82 _known_custom_db_types = tuple(mathesar_custom_type for mathesar_custom_type in MathesarCustomType)
83 
84 
85 # Known database types are those that are defined on our PostgresType and MathesarCustomType Enums.
86 known_db_types = _known_vanilla_db_types + _known_custom_db_types
87 
88 
89 # Origin: https://www.python.org/dev/peps/pep-0616/#id17
90 def _remove_prefix(self: str, prefix: str, /) -> str:
91     """
92     This will remove the passed prefix, if it's there.
93     Otherwise, it will return the string unchanged.
94     """
95     if self.startswith(prefix):
96         return self[len(prefix):]
97     else:
98         return self[:]
99 
100 
101 def get_db_type_enum_from_id(db_type_id):
102     """
103     Gets an instance of either the PostgresType enum or the MathesarCustomType enum corresponding
104     to the provided db_type_id. If the id doesn't correspond to any of the mentioned enums,
105     returns None.
106     """
107     try:
108         return PostgresType(db_type_id)
109     except ValueError:
110         try:
111             # Sometimes MA type identifiers are qualified like so: `mathesar_types.uri`.
112             # We want to remove that prefix, when it's there, because MathesarCustomType
113             # enum stores type ids without a qualifier (e.g. `uri`).
114             possible_prefix = _ma_type_qualifier_prefix + '.'
115             preprocessed_db_type_id = _remove_prefix(db_type_id, possible_prefix)
116             return MathesarCustomType(preprocessed_db_type_id)
117         except ValueError:
118             return None
119 
120 
121 def _build_db_types_hinted():
122     """
123     Builds up a map of db types to hintsets.
124     """
125     # Start out by defining some hints manually.
126     db_types_hinted = {
127         PostgresType.BOOLEAN: tuple([
128             hints.boolean
129         ]),
130         MathesarCustomType.URI: tuple([
131             hints.uri
132         ]),
133         MathesarCustomType.EMAIL: tuple([
134             hints.email
135         ]),
136     }
137 
138     # Then, start adding hints automatically.
139     # This is for many-to-many relationships, i.e. adding multiple identical hintsets to the
140     # hintsets of multiple db types.
141     def _add_to_db_type_hintsets(db_types, hints):
142         """
143         Mutates db_types_hinted to map every hint in `hints` to every DB type in `db_types`.
144         """
145         for db_type in db_types:
146             if db_type in db_types_hinted:
147                 updated_hintset = tuple(set(db_types_hinted[db_type] + tuple(hints)))
148                 db_types_hinted[db_type] = updated_hintset
149             else:
150                 db_types_hinted[db_type] = tuple(hints)
151 
152     # all types get the "any" hint
153     all_db_types = known_db_types
154     hints_for_all_db_types = (hints.any,)
155     _add_to_db_type_hintsets(all_db_types, hints_for_all_db_types)
156 
157     # string-like types get the "string_like" hint
158     string_like_db_types = (
159         PostgresType.CHARACTER_VARYING,
160         PostgresType.CHARACTER,
161         PostgresType.TEXT,
162         MathesarCustomType.URI,
163         MathesarCustomType.EMAIL,
164     )
165     hints_for_string_like_types = (hints.string_like,)
166     _add_to_db_type_hintsets(string_like_db_types, hints_for_string_like_types)
167 
168     # numeric types get the "comparable" hint
169     numeric_db_types = (
170         PostgresType.BIGINT,
171         PostgresType.DECIMAL,
172         PostgresType.DOUBLE_PRECISION,
173         PostgresType.FLOAT,
174         PostgresType.INTEGER,
175         PostgresType.SMALLINT,
176         PostgresType.NUMERIC,
177         PostgresType.REAL,
178     )
179     hints_for_numeric_db_types = (hints.comparable,)
180     _add_to_db_type_hintsets(numeric_db_types, hints_for_numeric_db_types)
181 
182     return frozendict(db_types_hinted)
183 
184 
185 db_types_hinted = _build_db_types_hinted()
186 
187 
188 SCHEMA = f"{constants.MATHESAR_PREFIX}types"
189 # Since we want to have our identifiers quoted appropriately for use in
190 # PostgreSQL, we want to use the postgres dialect preparer to set this up.
191 preparer = create_engine("postgresql://").dialect.identifier_preparer
192 
193 
194 # Should usually equal `mathesar_types`
195 _ma_type_qualifier_prefix = preparer.quote_schema(SCHEMA)
196 
197 
198 def get_qualified_name(name):
199     return ".".join([_ma_type_qualifier_prefix, name])
200 
201 
202 def get_available_types(engine):
203     """
204     Returns a dict where the keys are database type names defined on the database associated with
205     provided Engine, and the values are their SQLAlchemy classes.
206     """
207     return engine.dialect.ischema_names
208 
209 
210 def get_available_known_db_types(engine):
211     """
212     Returns database types that are both available on the database and known through our Enums
213     above.
214     """
215     available_db_types = get_available_types(engine)
216     return tuple(
217         known_db_type
218         for known_db_type in known_db_types
219         if known_db_type.value in available_db_types
220     )
221 
222 
223 def get_db_type_name(sa_type, engine):
224     try:
225         db_type = sa_type.compile(dialect=engine.dialect)
226     except TypeError:
227         db_type = sa_type().compile(dialect=engine.dialect)
228     return db_type
229
```
--- 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/types/base.py b/db/types/base.py
--- a/db/types/base.py
+++ b/db/types/base.py
@@ -156,8 +156,10 @@
 
     # string-like types get the "string_like" hint
     string_like_db_types = (
-        PostgresType.CHARACTER_VARYING,
+        PostgresType.CHAR,
         PostgresType.CHARACTER,
+        PostgresType.CHARACTER_VARYING,
+        PostgresType.NAME,
         PostgresType.TEXT,
         MathesarCustomType.URI,
         MathesarCustomType.EMAIL,
@@ -175,10 +177,25 @@
         PostgresType.SMALLINT,
         PostgresType.NUMERIC,
         PostgresType.REAL,
+        PostgresType.MONEY,
     )
     hints_for_numeric_db_types = (hints.comparable,)
     _add_to_db_type_hintsets(numeric_db_types, hints_for_numeric_db_types)
 
+    # time related types get the "comparable" hint
+    time_related_db_types = (
+        PostgresType.DATE,
+        PostgresType.TIME,
+        PostgresType.TIME_WITH_TIME_ZONE,
+        PostgresType.TIME_WITHOUT_TIME_ZONE,
+        PostgresType.TIMESTAMP,
+        PostgresType.TIMESTAMP_WITH_TIME_ZONE,
+        PostgresType.TIMESTAMP_WITHOUT_TIME_ZONE,
+        PostgresType.INTERVAL,
+    )
+    hints_for_time_related_types = (hints.comparable,)
+    _add_to_db_type_hintsets(time_related_db_types, hints_for_time_related_types)
+
     return frozendict(db_types_hinted)

{"golden_diff": "diff --git a/db/types/base.py b/db/types/base.py\n--- a/db/types/base.py\n+++ b/db/types/base.py\n@@ -156,8 +156,10 @@\n \n     # string-like types get the \"string_like\" hint\n     string_like_db_types = (\n-        PostgresType.CHARACTER_VARYING,\n+        PostgresType.CHAR,\n         PostgresType.CHARACTER,\n+        PostgresType.CHARACTER_VARYING,\n+        PostgresType.NAME,\n         PostgresType.TEXT,\n         MathesarCustomType.URI,\n         MathesarCustomType.EMAIL,\n@@ -175,10 +177,25 @@\n         PostgresType.SMALLINT,\n         PostgresType.NUMERIC,\n         PostgresType.REAL,\n+        PostgresType.MONEY,\n     )\n     hints_for_numeric_db_types = (hints.comparable,)\n     _add_to_db_type_hintsets(numeric_db_types, hints_for_numeric_db_types)\n \n+    # time related types get the \"comparable\" hint\n+    time_related_db_types = (\n+        PostgresType.DATE,\n+        PostgresType.TIME,\n+        PostgresType.TIME_WITH_TIME_ZONE,\n+        PostgresType.TIME_WITHOUT_TIME_ZONE,\n+        PostgresType.TIMESTAMP,\n+        PostgresType.TIMESTAMP_WITH_TIME_ZONE,\n+        PostgresType.TIMESTAMP_WITHOUT_TIME_ZONE,\n+        PostgresType.INTERVAL,\n+    )\n+    hints_for_time_related_types = (hints.comparable,)\n+    _add_to_db_type_hintsets(time_related_db_types, hints_for_time_related_types)\n+\n     return frozendict(db_types_hinted)\n", "issue": "Implement filtering options for date & time types\n## Problem\r\n- We need to ensure that records that include columns of all Date & Time types support the following filters via API: \r\n  - between {x} and {y}\r\n  - is {x}\r\n  - is not {x}\r\n  - before {x}\r\n  - after {x}\r\n  - on or before {x}\r\n  - or or after {x}\r\n  - is empty\r\n  - is not empty\r\n- We need to also ensure that values of the filters don't have to be an exact date. We should accept natural language like \"next month\", or \"tomorrow\".\r\n  - We could use https://dateparser.readthedocs.io/\r\n\r\nThis involves:\r\n- Implementing the filters in the backend\r\n- Updating the `/api/v0/databases/<id>/types/` endpoint to store available filters on this type\r\n  - Filter information should include the number of parameters needing to be passed in (e.g. `between` needs 2 parameters, `is empty` needs 0)\r\n\r\n## Additional context\r\n- We're using our fork of `sqlalchemy-filters` to provide filtering. See: https://github.com/centerofci/sqlalchemy-filters.\r\n- This issue is blocked by implementation of Date & Time types: \r\n  - #424 \r\n  - #425 \r\n  - #426\r\n- #557 provides some context on storing filters.\nImplement filtering options for duration type\n## Problem\r\n- We need to ensure that records that include columns of duration types support the following filters via API: \r\n  - between {x} and {y}\r\n  - equals {x}\r\n  - does not equal {x}\r\n  - greater than {x}\r\n  - less than {x}\r\n  - greater than or equals {x}\r\n  - less than or equals  {x}\r\n  - is empty\r\n  - is not empty\r\n- We need to also ensure that values of the filters don't have to be a number. We should accept natural language like \"an hour\", or \"2 days\".\r\n  - We could use something like https://github.com/oleiade/durations or https://github.com/wroberts/pytimeparse\r\n\r\nThis involves:\r\n- Implementing the filters in the backend\r\n- Updating the `/api/v0/databases/<id>/types/` endpoint to store available filters on this type\r\n  - Filter information should include the number of parameters needing to be passed in (e.g. `between` needs 2 parameters, `is empty` needs 0)\r\n\r\n## Additional context\r\n- These filters are the same as those for Number types. We should just reuse those.\r\n  - The only additional thing we need to implement is converting natural language to numbers.\r\n  - Relevant issue: #385 \r\n- We're using our fork of `sqlalchemy-filters` to provide filtering. See: https://github.com/centerofci/sqlalchemy-filters.\r\n- This issue is blocked by implementation of #430  \r\n- #557 provides some context on storing filters.\r\n\r\nMarking as blocked until #385 and #430 are complete.\n", "before_files": [{"content": "from enum import Enum\n\nfrom sqlalchemy import create_engine\n\nfrom db import constants\n\nfrom db.functions import hints\n\nfrom frozendict import frozendict\n\n\nCHAR = 'char'\nSTRING = 'string'\nVARCHAR = 'varchar'\n\n\nclass PostgresType(Enum):\n    \"\"\"\n    This only includes built-in Postgres types that SQLAlchemy supports.\n    SQLAlchemy doesn't support XML. See zzzeek's comment on:\n    https://stackoverflow.com/questions/16153512/using-postgresql-xml-data-type-with-sqlalchemy\n    The values are keys returned by get_available_types.\n    \"\"\"\n    _ARRAY = '_array'\n    BIGINT = 'bigint'\n    BIT_VARYING = 'bit varying'\n    BIT = 'bit'\n    BOOLEAN = 'boolean'\n    BYTEA = 'bytea'\n    CHAR = '\"char\"'\n    CHARACTER_VARYING = 'character varying'\n    CHARACTER = 'character'\n    CIDR = 'cidr'\n    DATE = 'date'\n    DATERANGE = 'daterange'\n    DECIMAL = 'decimal'\n    DOUBLE_PRECISION = 'double precision'\n    FLOAT = 'float'\n    HSTORE = 'hstore'\n    INET = 'inet'\n    INT4RANGE = 'int4range'\n    INT8RANGE = 'int8range'\n    INTEGER = 'integer'\n    INTERVAL = 'interval'\n    JSON = 'json'\n    JSONB = 'jsonb'\n    MACADDR = 'macaddr'\n    MONEY = 'money'\n    NAME = 'name'\n    NUMERIC = 'numeric'\n    NUMRANGE = 'numrange'\n    OID = 'oid'\n    REAL = 'real'\n    REGCLASS = 'regclass'\n    SMALLINT = 'smallint'\n    TEXT = 'text'\n    TIME = 'time'\n    TIME_WITH_TIME_ZONE = 'time with time zone'\n    TIME_WITHOUT_TIME_ZONE = 'time without time zone'\n    TIMESTAMP = 'timestamp'\n    TIMESTAMP_WITH_TIME_ZONE = 'timestamp with time zone'\n    TIMESTAMP_WITHOUT_TIME_ZONE = 'timestamp without time zone'\n    TSRANGE = 'tsrange'\n    TSTZRANGE = 'tstzrange'\n    TSVECTOR = 'tsvector'\n    UUID = 'uuid'\n\n\nclass MathesarCustomType(Enum):\n    \"\"\"\n    This is a list of custom Mathesar DB types.\n    Keys returned by get_available_types are of the format 'mathesar_types.VALUE'\n    \"\"\"\n    EMAIL = 'email'\n    URI = 'uri'\n    MATHESAR_MONEY = 'mathesar_money'\n\n\n_known_vanilla_db_types = tuple(postgres_type for postgres_type in PostgresType)\n\n\n_known_custom_db_types = tuple(mathesar_custom_type for mathesar_custom_type in MathesarCustomType)\n\n\n# Known database types are those that are defined on our PostgresType and MathesarCustomType Enums.\nknown_db_types = _known_vanilla_db_types + _known_custom_db_types\n\n\n# Origin: https://www.python.org/dev/peps/pep-0616/#id17\ndef _remove_prefix(self: str, prefix: str, /) -> str:\n    \"\"\"\n    This will remove the passed prefix, if it's there.\n    Otherwise, it will return the string unchanged.\n    \"\"\"\n    if self.startswith(prefix):\n        return self[len(prefix):]\n    else:\n        return self[:]\n\n\ndef get_db_type_enum_from_id(db_type_id):\n    \"\"\"\n    Gets an instance of either the PostgresType enum or the MathesarCustomType enum corresponding\n    to the provided db_type_id. If the id doesn't correspond to any of the mentioned enums,\n    returns None.\n    \"\"\"\n    try:\n        return PostgresType(db_type_id)\n    except ValueError:\n        try:\n            # Sometimes MA type identifiers are qualified like so: `mathesar_types.uri`.\n            # We want to remove that prefix, when it's there, because MathesarCustomType\n            # enum stores type ids without a qualifier (e.g. `uri`).\n            possible_prefix = _ma_type_qualifier_prefix + '.'\n            preprocessed_db_type_id = _remove_prefix(db_type_id, possible_prefix)\n            return MathesarCustomType(preprocessed_db_type_id)\n        except ValueError:\n            return None\n\n\ndef _build_db_types_hinted():\n    \"\"\"\n    Builds up a map of db types to hintsets.\n    \"\"\"\n    # Start out by defining some hints manually.\n    db_types_hinted = {\n        PostgresType.BOOLEAN: tuple([\n            hints.boolean\n        ]),\n        MathesarCustomType.URI: tuple([\n            hints.uri\n        ]),\n        MathesarCustomType.EMAIL: tuple([\n            hints.email\n        ]),\n    }\n\n    # Then, start adding hints automatically.\n    # This is for many-to-many relationships, i.e. adding multiple identical hintsets to the\n    # hintsets of multiple db types.\n    def _add_to_db_type_hintsets(db_types, hints):\n        \"\"\"\n        Mutates db_types_hinted to map every hint in `hints` to every DB type in `db_types`.\n        \"\"\"\n        for db_type in db_types:\n            if db_type in db_types_hinted:\n                updated_hintset = tuple(set(db_types_hinted[db_type] + tuple(hints)))\n                db_types_hinted[db_type] = updated_hintset\n            else:\n                db_types_hinted[db_type] = tuple(hints)\n\n    # all types get the \"any\" hint\n    all_db_types = known_db_types\n    hints_for_all_db_types = (hints.any,)\n    _add_to_db_type_hintsets(all_db_types, hints_for_all_db_types)\n\n    # string-like types get the \"string_like\" hint\n    string_like_db_types = (\n        PostgresType.CHARACTER_VARYING,\n        PostgresType.CHARACTER,\n        PostgresType.TEXT,\n        MathesarCustomType.URI,\n        MathesarCustomType.EMAIL,\n    )\n    hints_for_string_like_types = (hints.string_like,)\n    _add_to_db_type_hintsets(string_like_db_types, hints_for_string_like_types)\n\n    # numeric types get the \"comparable\" hint\n    numeric_db_types = (\n        PostgresType.BIGINT,\n        PostgresType.DECIMAL,\n        PostgresType.DOUBLE_PRECISION,\n        PostgresType.FLOAT,\n        PostgresType.INTEGER,\n        PostgresType.SMALLINT,\n        PostgresType.NUMERIC,\n        PostgresType.REAL,\n    )\n    hints_for_numeric_db_types = (hints.comparable,)\n    _add_to_db_type_hintsets(numeric_db_types, hints_for_numeric_db_types)\n\n    return frozendict(db_types_hinted)\n\n\ndb_types_hinted = _build_db_types_hinted()\n\n\nSCHEMA = f\"{constants.MATHESAR_PREFIX}types\"\n# Since we want to have our identifiers quoted appropriately for use in\n# PostgreSQL, we want to use the postgres dialect preparer to set this up.\npreparer = create_engine(\"postgresql://\").dialect.identifier_preparer\n\n\n# Should usually equal `mathesar_types`\n_ma_type_qualifier_prefix = preparer.quote_schema(SCHEMA)\n\n\ndef get_qualified_name(name):\n    return \".\".join([_ma_type_qualifier_prefix, name])\n\n\ndef get_available_types(engine):\n    \"\"\"\n    Returns a dict where the keys are database type names defined on the database associated with\n    provided Engine, and the values are their SQLAlchemy classes.\n    \"\"\"\n    return engine.dialect.ischema_names\n\n\ndef get_available_known_db_types(engine):\n    \"\"\"\n    Returns database types that are both available on the database and known through our Enums\n    above.\n    \"\"\"\n    available_db_types = get_available_types(engine)\n    return tuple(\n        known_db_type\n        for known_db_type in known_db_types\n        if known_db_type.value in available_db_types\n    )\n\n\ndef get_db_type_name(sa_type, engine):\n    try:\n        db_type = sa_type.compile(dialect=engine.dialect)\n    except TypeError:\n        db_type = sa_type().compile(dialect=engine.dialect)\n    return db_type\n", "path": "db/types/base.py"}], "after_files": [{"content": "from enum import Enum\n\nfrom sqlalchemy import create_engine\n\nfrom db import constants\n\nfrom db.functions import hints\n\nfrom frozendict import frozendict\n\n\nCHAR = 'char'\nSTRING = 'string'\nVARCHAR = 'varchar'\n\n\nclass PostgresType(Enum):\n    \"\"\"\n    This only includes built-in Postgres types that SQLAlchemy supports.\n    SQLAlchemy doesn't support XML. See zzzeek's comment on:\n    https://stackoverflow.com/questions/16153512/using-postgresql-xml-data-type-with-sqlalchemy\n    The values are keys returned by get_available_types.\n    \"\"\"\n    _ARRAY = '_array'\n    BIGINT = 'bigint'\n    BIT_VARYING = 'bit varying'\n    BIT = 'bit'\n    BOOLEAN = 'boolean'\n    BYTEA = 'bytea'\n    CHAR = '\"char\"'\n    CHARACTER_VARYING = 'character varying'\n    CHARACTER = 'character'\n    CIDR = 'cidr'\n    DATE = 'date'\n    DATERANGE = 'daterange'\n    DECIMAL = 'decimal'\n    DOUBLE_PRECISION = 'double precision'\n    FLOAT = 'float'\n    HSTORE = 'hstore'\n    INET = 'inet'\n    INT4RANGE = 'int4range'\n    INT8RANGE = 'int8range'\n    INTEGER = 'integer'\n    INTERVAL = 'interval'\n    JSON = 'json'\n    JSONB = 'jsonb'\n    MACADDR = 'macaddr'\n    MONEY = 'money'\n    NAME = 'name'\n    NUMERIC = 'numeric'\n    NUMRANGE = 'numrange'\n    OID = 'oid'\n    REAL = 'real'\n    REGCLASS = 'regclass'\n    SMALLINT = 'smallint'\n    TEXT = 'text'\n    TIME = 'time'\n    TIME_WITH_TIME_ZONE = 'time with time zone'\n    TIME_WITHOUT_TIME_ZONE = 'time without time zone'\n    TIMESTAMP = 'timestamp'\n    TIMESTAMP_WITH_TIME_ZONE = 'timestamp with time zone'\n    TIMESTAMP_WITHOUT_TIME_ZONE = 'timestamp without time zone'\n    TSRANGE = 'tsrange'\n    TSTZRANGE = 'tstzrange'\n    TSVECTOR = 'tsvector'\n    UUID = 'uuid'\n\n\nclass MathesarCustomType(Enum):\n    \"\"\"\n    This is a list of custom Mathesar DB types.\n    Keys returned by get_available_types are of the format 'mathesar_types.VALUE'\n    \"\"\"\n    EMAIL = 'email'\n    URI = 'uri'\n    MATHESAR_MONEY = 'mathesar_money'\n\n\n_known_vanilla_db_types = tuple(postgres_type for postgres_type in PostgresType)\n\n\n_known_custom_db_types = tuple(mathesar_custom_type for mathesar_custom_type in MathesarCustomType)\n\n\n# Known database types are those that are defined on our PostgresType and MathesarCustomType Enums.\nknown_db_types = _known_vanilla_db_types + _known_custom_db_types\n\n\n# Origin: https://www.python.org/dev/peps/pep-0616/#id17\ndef _remove_prefix(self: str, prefix: str, /) -> str:\n    \"\"\"\n    This will remove the passed prefix, if it's there.\n    Otherwise, it will return the string unchanged.\n    \"\"\"\n    if self.startswith(prefix):\n        return self[len(prefix):]\n    else:\n        return self[:]\n\n\ndef get_db_type_enum_from_id(db_type_id):\n    \"\"\"\n    Gets an instance of either the PostgresType enum or the MathesarCustomType enum corresponding\n    to the provided db_type_id. If the id doesn't correspond to any of the mentioned enums,\n    returns None.\n    \"\"\"\n    try:\n        return PostgresType(db_type_id)\n    except ValueError:\n        try:\n            # Sometimes MA type identifiers are qualified like so: `mathesar_types.uri`.\n            # We want to remove that prefix, when it's there, because MathesarCustomType\n            # enum stores type ids without a qualifier (e.g. `uri`).\n            possible_prefix = _ma_type_qualifier_prefix + '.'\n            preprocessed_db_type_id = _remove_prefix(db_type_id, possible_prefix)\n            return MathesarCustomType(preprocessed_db_type_id)\n        except ValueError:\n            return None\n\n\ndef _build_db_types_hinted():\n    \"\"\"\n    Builds up a map of db types to hintsets.\n    \"\"\"\n    # Start out by defining some hints manually.\n    db_types_hinted = {\n        PostgresType.BOOLEAN: tuple([\n            hints.boolean\n        ]),\n        MathesarCustomType.URI: tuple([\n            hints.uri\n        ]),\n        MathesarCustomType.EMAIL: tuple([\n            hints.email\n        ]),\n    }\n\n    # Then, start adding hints automatically.\n    # This is for many-to-many relationships, i.e. adding multiple identical hintsets to the\n    # hintsets of multiple db types.\n    def _add_to_db_type_hintsets(db_types, hints):\n        \"\"\"\n        Mutates db_types_hinted to map every hint in `hints` to every DB type in `db_types`.\n        \"\"\"\n        for db_type in db_types:\n            if db_type in db_types_hinted:\n                updated_hintset = tuple(set(db_types_hinted[db_type] + tuple(hints)))\n                db_types_hinted[db_type] = updated_hintset\n            else:\n                db_types_hinted[db_type] = tuple(hints)\n\n    # all types get the \"any\" hint\n    all_db_types = known_db_types\n    hints_for_all_db_types = (hints.any,)\n    _add_to_db_type_hintsets(all_db_types, hints_for_all_db_types)\n\n    # string-like types get the \"string_like\" hint\n    string_like_db_types = (\n        PostgresType.CHAR,\n        PostgresType.CHARACTER,\n        PostgresType.CHARACTER_VARYING,\n        PostgresType.NAME,\n        PostgresType.TEXT,\n        MathesarCustomType.URI,\n        MathesarCustomType.EMAIL,\n    )\n    hints_for_string_like_types = (hints.string_like,)\n    _add_to_db_type_hintsets(string_like_db_types, hints_for_string_like_types)\n\n    # numeric types get the \"comparable\" hint\n    numeric_db_types = (\n        PostgresType.BIGINT,\n        PostgresType.DECIMAL,\n        PostgresType.DOUBLE_PRECISION,\n        PostgresType.FLOAT,\n        PostgresType.INTEGER,\n        PostgresType.SMALLINT,\n        PostgresType.NUMERIC,\n        PostgresType.REAL,\n        PostgresType.MONEY,\n    )\n    hints_for_numeric_db_types = (hints.comparable,)\n    _add_to_db_type_hintsets(numeric_db_types, hints_for_numeric_db_types)\n\n    # time related types get the \"comparable\" hint\n    time_related_db_types = (\n        PostgresType.DATE,\n        PostgresType.TIME,\n        PostgresType.TIME_WITH_TIME_ZONE,\n        PostgresType.TIME_WITHOUT_TIME_ZONE,\n        PostgresType.TIMESTAMP,\n        PostgresType.TIMESTAMP_WITH_TIME_ZONE,\n        PostgresType.TIMESTAMP_WITHOUT_TIME_ZONE,\n        PostgresType.INTERVAL,\n    )\n    hints_for_time_related_types = (hints.comparable,)\n    _add_to_db_type_hintsets(time_related_db_types, hints_for_time_related_types)\n\n    return frozendict(db_types_hinted)\n\n\ndb_types_hinted = _build_db_types_hinted()\n\n\nSCHEMA = f\"{constants.MATHESAR_PREFIX}types\"\n# Since we want to have our identifiers quoted appropriately for use in\n# PostgreSQL, we want to use the postgres dialect preparer to set this up.\npreparer = create_engine(\"postgresql://\").dialect.identifier_preparer\n\n\n# Should usually equal `mathesar_types`\n_ma_type_qualifier_prefix = preparer.quote_schema(SCHEMA)\n\n\ndef get_qualified_name(name):\n    return \".\".join([_ma_type_qualifier_prefix, name])\n\n\ndef get_available_types(engine):\n    \"\"\"\n    Returns a dict where the keys are database type names defined on the database associated with\n    provided Engine, and the values are their SQLAlchemy classes.\n    \"\"\"\n    return engine.dialect.ischema_names\n\n\ndef get_available_known_db_types(engine):\n    \"\"\"\n    Returns database types that are both available on the database and known through our Enums\n    above.\n    \"\"\"\n    available_db_types = get_available_types(engine)\n    return tuple(\n        known_db_type\n        for known_db_type in known_db_types\n        if known_db_type.value in available_db_types\n    )\n\n\ndef get_db_type_name(sa_type, engine):\n    try:\n        db_type = sa_type.compile(dialect=engine.dialect)\n    except TypeError:\n        db_type = sa_type().compile(dialect=engine.dialect)\n    return db_type\n", "path": "db/types/base.py"}]}