We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support Importing Semicolon Separated Values file 
## Problem
Currently Mathesar allows importing [DSV](https://en.wikipedia.org/wiki/Delimiter-separated_values) files with following delimiters:
`,`
 `\t`
 `:`
`|`
Apart from them, semicolons`;` are popular delimiters used in industries (as address and integer generally contain commas).
## Proposed solution
It might be helpful if mathesar allows the user to import data from **semicolon-separated values** files as well.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mathesar/imports/csv.py`
Content:
```
1 from io import TextIOWrapper
2 
3 import clevercsv as csv
4 
5 from db.identifiers import truncate_if_necessary
6 from db.tables.operations.alter import update_pk_sequence_to_latest
7 from mathesar.database.base import create_mathesar_engine
8 from mathesar.models.base import Table
9 from db.records.operations.insert import insert_records_from_csv
10 from db.tables.operations.create import create_string_column_table
11 from db.tables.operations.select import get_oid_from_table
12 from db.tables.operations.drop import drop_table
13 from mathesar.errors import InvalidTableError
14 from db.constants import ID, ID_ORIGINAL, COLUMN_NAME_TEMPLATE
15 from psycopg2.errors import IntegrityError, DataError
16 
17 from mathesar.state import reset_reflection
18 
19 ALLOWED_DELIMITERS = ",\t:|"
20 SAMPLE_SIZE = 20000
21 CHECK_ROWS = 10
22 
23 
24 def get_file_encoding(file):
25     """
26     Given a file, uses charset_normalizer if installed or chardet which is installed as part of clevercsv module to
27     detect the file encoding. Returns a default value of utf-8-sig if encoding could not be detected or detection
28     libraries are missing.
29     """
30     from charset_normalizer import detect
31     # Sample Size reduces the accuracy
32     encoding = detect(file.read()).get('encoding', None)
33     file.seek(0)
34     if encoding is not None:
35         return encoding
36     return "utf-8"
37 
38 
39 def check_dialect(file, dialect):
40     """
41     Checks to see if we can parse the given file with the given dialect
42 
43     Parses the first CHECK_ROWS rows. Checks to see if any have formatting issues (as
44     indicated by parse_row), or if any have a differing number of columns.
45 
46     Args:
47         file: _io.TextIOWrapper object, an already opened file
48         dialect: csv.Dialect object, the dialect we are validating
49 
50     Returns:
51         bool: False if any error that would cause SQL errors were found, otherwise True
52     """
53     prev_num_columns = None
54     row_gen = csv.read.reader(file, dialect)
55     for _ in range(CHECK_ROWS):
56         try:
57             row = next(row_gen)
58         except StopIteration:
59             # If less than CHECK_ROWS rows in file, stop early
60             break
61 
62         num_columns = len(row)
63         if prev_num_columns is None:
64             prev_num_columns = num_columns
65         elif prev_num_columns != num_columns:
66             return False
67     return True
68 
69 
70 def get_sv_dialect(file):
71     """
72     Given a *sv file, generate a dialect to parse it.
73 
74     Args:
75         file: _io.TextIOWrapper object, an already opened file
76 
77     Returns:
78         dialect: csv.Dialect object, the dialect to parse the file
79 
80     Raises:
81         InvalidTableError: If the generated dialect was unable to parse the file
82     """
83     dialect = csv.detect.Detector().detect(file.read(SAMPLE_SIZE),
84                                            delimiters=ALLOWED_DELIMITERS)
85     if dialect is None:
86         raise InvalidTableError
87 
88     file.seek(0)
89     if check_dialect(file, dialect):
90         file.seek(0)
91         return dialect
92     else:
93         raise InvalidTableError
94 
95 
96 def get_sv_reader(file, header, dialect=None):
97     encoding = get_file_encoding(file)
98     file = TextIOWrapper(file, encoding=encoding)
99     if dialect:
100         reader = csv.DictReader(file, dialect=dialect)
101     else:
102         reader = csv.DictReader(file)
103     if not header:
104         reader.fieldnames = [
105             f"{COLUMN_NAME_TEMPLATE}{i}" for i in range(len(reader.fieldnames))
106         ]
107         file.seek(0)
108 
109     return reader
110 
111 
112 def create_db_table_from_data_file(data_file, name, schema, comment=None):
113     db_name = schema.database.name
114     engine = create_mathesar_engine(db_name)
115     sv_filename = data_file.file.path
116     header = data_file.header
117     dialect = csv.dialect.SimpleDialect(data_file.delimiter, data_file.quotechar,
118                                         data_file.escapechar)
119     encoding = get_file_encoding(data_file.file)
120     with open(sv_filename, 'rb') as sv_file:
121         sv_reader = get_sv_reader(sv_file, header, dialect=dialect)
122         column_names = _process_column_names(sv_reader.fieldnames)
123         table = create_string_column_table(
124             name=name,
125             schema=schema.name,
126             column_names=column_names,
127             engine=engine,
128             comment=comment,
129         )
130     try:
131         insert_records_from_csv(
132             table,
133             engine,
134             sv_filename,
135             column_names,
136             header,
137             delimiter=dialect.delimiter,
138             escape=dialect.escapechar,
139             quote=dialect.quotechar,
140             encoding=encoding
141         )
142         update_pk_sequence_to_latest(engine, table)
143     except (IntegrityError, DataError):
144         drop_table(name=name, schema=schema.name, engine=engine)
145         column_names_alt = [
146             column_name if column_name != ID else ID_ORIGINAL
147             for column_name in column_names
148         ]
149         table = create_string_column_table(
150             name=name,
151             schema=schema.name,
152             column_names=column_names_alt,
153             engine=engine,
154             comment=comment,
155         )
156         insert_records_from_csv(
157             table,
158             engine,
159             sv_filename,
160             column_names_alt,
161             header,
162             delimiter=dialect.delimiter,
163             escape=dialect.escapechar,
164             quote=dialect.quotechar,
165             encoding=encoding
166         )
167     reset_reflection(db_name=db_name)
168     return table
169 
170 
171 def _process_column_names(column_names):
172     column_names = (
173         column_name.strip()
174         for column_name
175         in column_names
176     )
177     column_names = (
178         truncate_if_necessary(column_name)
179         for column_name
180         in column_names
181     )
182     column_names = (
183         f"{COLUMN_NAME_TEMPLATE}{i}" if name == '' else name
184         for i, name
185         in enumerate(column_names)
186     )
187     return list(column_names)
188 
189 
190 def create_table_from_csv(data_file, name, schema, comment=None):
191     engine = create_mathesar_engine(schema.database.name)
192     db_table = create_db_table_from_data_file(
193         data_file, name, schema, comment=comment
194     )
195     db_table_oid = get_oid_from_table(db_table.name, db_table.schema, engine)
196     # Using current_objects to create the table instead of objects. objects
197     # triggers re-reflection, which will cause a race condition to create the table
198     table = Table.current_objects.get(
199         oid=db_table_oid,
200         schema=schema,
201     )
202     table.import_verified = False
203     table.save()
204     data_file.table_imported_to = table
205     data_file.save()
206     return table
207
```
--- 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/mathesar/imports/csv.py b/mathesar/imports/csv.py
--- a/mathesar/imports/csv.py
+++ b/mathesar/imports/csv.py
@@ -16,7 +16,7 @@
 
 from mathesar.state import reset_reflection
 
-ALLOWED_DELIMITERS = ",\t:|"
+ALLOWED_DELIMITERS = ",\t:|;"
 SAMPLE_SIZE = 20000
 CHECK_ROWS = 10

{"golden_diff": "diff --git a/mathesar/imports/csv.py b/mathesar/imports/csv.py\n--- a/mathesar/imports/csv.py\n+++ b/mathesar/imports/csv.py\n@@ -16,7 +16,7 @@\n \n from mathesar.state import reset_reflection\n \n-ALLOWED_DELIMITERS = \",\\t:|\"\n+ALLOWED_DELIMITERS = \",\\t:|;\"\n SAMPLE_SIZE = 20000\n CHECK_ROWS = 10\n", "issue": "Support Importing Semicolon Separated Values file \n## Problem\r\nCurrently Mathesar allows importing [DSV](https://en.wikipedia.org/wiki/Delimiter-separated_values) files with following delimiters:\r\n`,`\r\n `\\t`\r\n `:`\r\n`|`\r\nApart from them, semicolons`;` are popular delimiters used in industries (as address and integer generally contain commas).\r\n## Proposed solution\r\nIt might be helpful if mathesar allows the user to import data from **semicolon-separated values** files as well.\r\n\n", "before_files": [{"content": "from io import TextIOWrapper\n\nimport clevercsv as csv\n\nfrom db.identifiers import truncate_if_necessary\nfrom db.tables.operations.alter import update_pk_sequence_to_latest\nfrom mathesar.database.base import create_mathesar_engine\nfrom mathesar.models.base import Table\nfrom db.records.operations.insert import insert_records_from_csv\nfrom db.tables.operations.create import create_string_column_table\nfrom db.tables.operations.select import get_oid_from_table\nfrom db.tables.operations.drop import drop_table\nfrom mathesar.errors import InvalidTableError\nfrom db.constants import ID, ID_ORIGINAL, COLUMN_NAME_TEMPLATE\nfrom psycopg2.errors import IntegrityError, DataError\n\nfrom mathesar.state import reset_reflection\n\nALLOWED_DELIMITERS = \",\\t:|\"\nSAMPLE_SIZE = 20000\nCHECK_ROWS = 10\n\n\ndef get_file_encoding(file):\n    \"\"\"\n    Given a file, uses charset_normalizer if installed or chardet which is installed as part of clevercsv module to\n    detect the file encoding. Returns a default value of utf-8-sig if encoding could not be detected or detection\n    libraries are missing.\n    \"\"\"\n    from charset_normalizer import detect\n    # Sample Size reduces the accuracy\n    encoding = detect(file.read()).get('encoding', None)\n    file.seek(0)\n    if encoding is not None:\n        return encoding\n    return \"utf-8\"\n\n\ndef check_dialect(file, dialect):\n    \"\"\"\n    Checks to see if we can parse the given file with the given dialect\n\n    Parses the first CHECK_ROWS rows. Checks to see if any have formatting issues (as\n    indicated by parse_row), or if any have a differing number of columns.\n\n    Args:\n        file: _io.TextIOWrapper object, an already opened file\n        dialect: csv.Dialect object, the dialect we are validating\n\n    Returns:\n        bool: False if any error that would cause SQL errors were found, otherwise True\n    \"\"\"\n    prev_num_columns = None\n    row_gen = csv.read.reader(file, dialect)\n    for _ in range(CHECK_ROWS):\n        try:\n            row = next(row_gen)\n        except StopIteration:\n            # If less than CHECK_ROWS rows in file, stop early\n            break\n\n        num_columns = len(row)\n        if prev_num_columns is None:\n            prev_num_columns = num_columns\n        elif prev_num_columns != num_columns:\n            return False\n    return True\n\n\ndef get_sv_dialect(file):\n    \"\"\"\n    Given a *sv file, generate a dialect to parse it.\n\n    Args:\n        file: _io.TextIOWrapper object, an already opened file\n\n    Returns:\n        dialect: csv.Dialect object, the dialect to parse the file\n\n    Raises:\n        InvalidTableError: If the generated dialect was unable to parse the file\n    \"\"\"\n    dialect = csv.detect.Detector().detect(file.read(SAMPLE_SIZE),\n                                           delimiters=ALLOWED_DELIMITERS)\n    if dialect is None:\n        raise InvalidTableError\n\n    file.seek(0)\n    if check_dialect(file, dialect):\n        file.seek(0)\n        return dialect\n    else:\n        raise InvalidTableError\n\n\ndef get_sv_reader(file, header, dialect=None):\n    encoding = get_file_encoding(file)\n    file = TextIOWrapper(file, encoding=encoding)\n    if dialect:\n        reader = csv.DictReader(file, dialect=dialect)\n    else:\n        reader = csv.DictReader(file)\n    if not header:\n        reader.fieldnames = [\n            f\"{COLUMN_NAME_TEMPLATE}{i}\" for i in range(len(reader.fieldnames))\n        ]\n        file.seek(0)\n\n    return reader\n\n\ndef create_db_table_from_data_file(data_file, name, schema, comment=None):\n    db_name = schema.database.name\n    engine = create_mathesar_engine(db_name)\n    sv_filename = data_file.file.path\n    header = data_file.header\n    dialect = csv.dialect.SimpleDialect(data_file.delimiter, data_file.quotechar,\n                                        data_file.escapechar)\n    encoding = get_file_encoding(data_file.file)\n    with open(sv_filename, 'rb') as sv_file:\n        sv_reader = get_sv_reader(sv_file, header, dialect=dialect)\n        column_names = _process_column_names(sv_reader.fieldnames)\n        table = create_string_column_table(\n            name=name,\n            schema=schema.name,\n            column_names=column_names,\n            engine=engine,\n            comment=comment,\n        )\n    try:\n        insert_records_from_csv(\n            table,\n            engine,\n            sv_filename,\n            column_names,\n            header,\n            delimiter=dialect.delimiter,\n            escape=dialect.escapechar,\n            quote=dialect.quotechar,\n            encoding=encoding\n        )\n        update_pk_sequence_to_latest(engine, table)\n    except (IntegrityError, DataError):\n        drop_table(name=name, schema=schema.name, engine=engine)\n        column_names_alt = [\n            column_name if column_name != ID else ID_ORIGINAL\n            for column_name in column_names\n        ]\n        table = create_string_column_table(\n            name=name,\n            schema=schema.name,\n            column_names=column_names_alt,\n            engine=engine,\n            comment=comment,\n        )\n        insert_records_from_csv(\n            table,\n            engine,\n            sv_filename,\n            column_names_alt,\n            header,\n            delimiter=dialect.delimiter,\n            escape=dialect.escapechar,\n            quote=dialect.quotechar,\n            encoding=encoding\n        )\n    reset_reflection(db_name=db_name)\n    return table\n\n\ndef _process_column_names(column_names):\n    column_names = (\n        column_name.strip()\n        for column_name\n        in column_names\n    )\n    column_names = (\n        truncate_if_necessary(column_name)\n        for column_name\n        in column_names\n    )\n    column_names = (\n        f\"{COLUMN_NAME_TEMPLATE}{i}\" if name == '' else name\n        for i, name\n        in enumerate(column_names)\n    )\n    return list(column_names)\n\n\ndef create_table_from_csv(data_file, name, schema, comment=None):\n    engine = create_mathesar_engine(schema.database.name)\n    db_table = create_db_table_from_data_file(\n        data_file, name, schema, comment=comment\n    )\n    db_table_oid = get_oid_from_table(db_table.name, db_table.schema, engine)\n    # Using current_objects to create the table instead of objects. objects\n    # triggers re-reflection, which will cause a race condition to create the table\n    table = Table.current_objects.get(\n        oid=db_table_oid,\n        schema=schema,\n    )\n    table.import_verified = False\n    table.save()\n    data_file.table_imported_to = table\n    data_file.save()\n    return table\n", "path": "mathesar/imports/csv.py"}], "after_files": [{"content": "from io import TextIOWrapper\n\nimport clevercsv as csv\n\nfrom db.identifiers import truncate_if_necessary\nfrom db.tables.operations.alter import update_pk_sequence_to_latest\nfrom mathesar.database.base import create_mathesar_engine\nfrom mathesar.models.base import Table\nfrom db.records.operations.insert import insert_records_from_csv\nfrom db.tables.operations.create import create_string_column_table\nfrom db.tables.operations.select import get_oid_from_table\nfrom db.tables.operations.drop import drop_table\nfrom mathesar.errors import InvalidTableError\nfrom db.constants import ID, ID_ORIGINAL, COLUMN_NAME_TEMPLATE\nfrom psycopg2.errors import IntegrityError, DataError\n\nfrom mathesar.state import reset_reflection\n\nALLOWED_DELIMITERS = \",\\t:|;\"\nSAMPLE_SIZE = 20000\nCHECK_ROWS = 10\n\n\ndef get_file_encoding(file):\n    \"\"\"\n    Given a file, uses charset_normalizer if installed or chardet which is installed as part of clevercsv module to\n    detect the file encoding. Returns a default value of utf-8-sig if encoding could not be detected or detection\n    libraries are missing.\n    \"\"\"\n    from charset_normalizer import detect\n    # Sample Size reduces the accuracy\n    encoding = detect(file.read()).get('encoding', None)\n    file.seek(0)\n    if encoding is not None:\n        return encoding\n    return \"utf-8\"\n\n\ndef check_dialect(file, dialect):\n    \"\"\"\n    Checks to see if we can parse the given file with the given dialect\n\n    Parses the first CHECK_ROWS rows. Checks to see if any have formatting issues (as\n    indicated by parse_row), or if any have a differing number of columns.\n\n    Args:\n        file: _io.TextIOWrapper object, an already opened file\n        dialect: csv.Dialect object, the dialect we are validating\n\n    Returns:\n        bool: False if any error that would cause SQL errors were found, otherwise True\n    \"\"\"\n    prev_num_columns = None\n    row_gen = csv.read.reader(file, dialect)\n    for _ in range(CHECK_ROWS):\n        try:\n            row = next(row_gen)\n        except StopIteration:\n            # If less than CHECK_ROWS rows in file, stop early\n            break\n\n        num_columns = len(row)\n        if prev_num_columns is None:\n            prev_num_columns = num_columns\n        elif prev_num_columns != num_columns:\n            return False\n    return True\n\n\ndef get_sv_dialect(file):\n    \"\"\"\n    Given a *sv file, generate a dialect to parse it.\n\n    Args:\n        file: _io.TextIOWrapper object, an already opened file\n\n    Returns:\n        dialect: csv.Dialect object, the dialect to parse the file\n\n    Raises:\n        InvalidTableError: If the generated dialect was unable to parse the file\n    \"\"\"\n    dialect = csv.detect.Detector().detect(file.read(SAMPLE_SIZE),\n                                           delimiters=ALLOWED_DELIMITERS)\n    if dialect is None:\n        raise InvalidTableError\n\n    file.seek(0)\n    if check_dialect(file, dialect):\n        file.seek(0)\n        return dialect\n    else:\n        raise InvalidTableError\n\n\ndef get_sv_reader(file, header, dialect=None):\n    encoding = get_file_encoding(file)\n    file = TextIOWrapper(file, encoding=encoding)\n    if dialect:\n        reader = csv.DictReader(file, dialect=dialect)\n    else:\n        reader = csv.DictReader(file)\n    if not header:\n        reader.fieldnames = [\n            f\"{COLUMN_NAME_TEMPLATE}{i}\" for i in range(len(reader.fieldnames))\n        ]\n        file.seek(0)\n\n    return reader\n\n\ndef create_db_table_from_data_file(data_file, name, schema, comment=None):\n    db_name = schema.database.name\n    engine = create_mathesar_engine(db_name)\n    sv_filename = data_file.file.path\n    header = data_file.header\n    dialect = csv.dialect.SimpleDialect(data_file.delimiter, data_file.quotechar,\n                                        data_file.escapechar)\n    encoding = get_file_encoding(data_file.file)\n    with open(sv_filename, 'rb') as sv_file:\n        sv_reader = get_sv_reader(sv_file, header, dialect=dialect)\n        column_names = _process_column_names(sv_reader.fieldnames)\n        table = create_string_column_table(\n            name=name,\n            schema=schema.name,\n            column_names=column_names,\n            engine=engine,\n            comment=comment,\n        )\n    try:\n        insert_records_from_csv(\n            table,\n            engine,\n            sv_filename,\n            column_names,\n            header,\n            delimiter=dialect.delimiter,\n            escape=dialect.escapechar,\n            quote=dialect.quotechar,\n            encoding=encoding\n        )\n        update_pk_sequence_to_latest(engine, table)\n    except (IntegrityError, DataError):\n        drop_table(name=name, schema=schema.name, engine=engine)\n        column_names_alt = [\n            column_name if column_name != ID else ID_ORIGINAL\n            for column_name in column_names\n        ]\n        table = create_string_column_table(\n            name=name,\n            schema=schema.name,\n            column_names=column_names_alt,\n            engine=engine,\n            comment=comment,\n        )\n        insert_records_from_csv(\n            table,\n            engine,\n            sv_filename,\n            column_names_alt,\n            header,\n            delimiter=dialect.delimiter,\n            escape=dialect.escapechar,\n            quote=dialect.quotechar,\n            encoding=encoding\n        )\n    reset_reflection(db_name=db_name)\n    return table\n\n\ndef _process_column_names(column_names):\n    column_names = (\n        column_name.strip()\n        for column_name\n        in column_names\n    )\n    column_names = (\n        truncate_if_necessary(column_name)\n        for column_name\n        in column_names\n    )\n    column_names = (\n        f\"{COLUMN_NAME_TEMPLATE}{i}\" if name == '' else name\n        for i, name\n        in enumerate(column_names)\n    )\n    return list(column_names)\n\n\ndef create_table_from_csv(data_file, name, schema, comment=None):\n    engine = create_mathesar_engine(schema.database.name)\n    db_table = create_db_table_from_data_file(\n        data_file, name, schema, comment=comment\n    )\n    db_table_oid = get_oid_from_table(db_table.name, db_table.schema, engine)\n    # Using current_objects to create the table instead of objects. objects\n    # triggers re-reflection, which will cause a race condition to create the table\n    table = Table.current_objects.get(\n        oid=db_table_oid,\n        schema=schema,\n    )\n    table.import_verified = False\n    table.save()\n    data_file.table_imported_to = table\n    data_file.save()\n    return table\n", "path": "mathesar/imports/csv.py"}]}