We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Test all code brought in from the prototype
**Problem**
<!-- Please provide a clear and concise description of the problem that this feature request is designed to solve.-->
The code brought over from the prototype in PRs #57 , #60, #75, #79, #85 is not yet fully tested

**Proposed solution**
<!-- A clear and concise description of your proposed solution or feature. -->

Write tests covering all code from those PRs which is not yet covered.


--- 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/tables.py`
Content:
```
1 from sqlalchemy import (
2     Column, String, Table, MetaData, func, select, ForeignKey, literal, exists
3 )
4 
5 from db import columns, constants, schemas
6 
7 
8 def create_string_column_table(name, schema, column_names, engine):
9     """
10     This method creates a Postgres table in the specified schema, with all
11     columns being String type.
12     """
13     columns_ = [Column(column_name, String) for column_name in column_names]
14     table = create_mathesar_table(name, schema, columns_, engine)
15     return table
16 
17 
18 def create_mathesar_table(name, schema, columns_, engine, metadata=None):
19     """
20     This method creates a Postgres table in the specified schema using the
21     given name and column list.  It adds internal mathesar columns to the
22     table.
23     """
24     columns_ = columns.init_mathesar_table_column_list_with_defaults(columns_)
25     schemas.create_schema(schema, engine)
26     # We need this so that we can create multiple mathesar tables in the
27     # same MetaData, enabling them to reference each other in the
28     # SQLAlchemy context (e.g., for creating a ForeignKey relationship)
29     if metadata is None:
30         metadata = MetaData(bind=engine, schema=schema)
31     # This reflection step lets us notice any "table already exists"
32     # errors before sending error-generating requests to the DB.
33     metadata.reflect()
34     table = Table(
35         name,
36         metadata,
37         *columns_,
38         schema=schema,
39     )
40     table.create(engine)
41     return table
42 
43 
44 def extract_columns_from_table(
45         old_table_name,
46         extracted_column_names,
47         extracted_table_name,
48         remainder_table_name,
49         schema,
50         engine,
51         drop_original_table=False,
52 ):
53     old_table = reflect_table(old_table_name, schema, engine)
54     old_columns = (
55         columns.MathesarColumn.from_column(col) for col in old_table.columns
56     )
57     old_non_default_columns = [
58         col for col in old_columns if not col.is_default
59     ]
60     extracted_columns, remainder_columns = _split_column_list(
61         old_non_default_columns, extracted_column_names,
62     )
63     with engine.begin() as conn:
64         extracted_table, remainder_table, remainder_fk = _create_split_tables(
65             extracted_table_name,
66             extracted_columns,
67             remainder_table_name,
68             remainder_columns,
69             schema,
70             engine,
71         )
72         split_ins = _create_split_insert_stmt(
73             old_table,
74             extracted_table,
75             extracted_columns,
76             remainder_table,
77             remainder_columns,
78             remainder_fk,
79         )
80         conn.execute(split_ins)
81     if drop_original_table:
82         old_table.drop()
83 
84     return extracted_table, remainder_table, remainder_fk
85 
86 
87 def _split_column_list(columns_, extracted_column_names):
88     extracted_columns = [
89         col for col in columns_ if col.name in extracted_column_names
90     ]
91     remainder_columns = [
92         col for col in columns_ if col.name not in extracted_column_names
93     ]
94     return extracted_columns, remainder_columns
95 
96 
97 def _create_split_tables(
98         extracted_table_name,
99         extracted_columns,
100         remainder_table_name,
101         remainder_columns,
102         schema,
103         engine,
104 ):
105     extracted_table = create_mathesar_table(
106         extracted_table_name,
107         schema,
108         extracted_columns,
109         engine,
110     )
111     remainder_fk_column = Column(
112         f"{extracted_table.name}_{constants.ID}",
113         columns.ID_TYPE,
114         ForeignKey(f"{extracted_table.name}.{constants.ID}"),
115         nullable=False,
116     )
117     remainder_table = create_mathesar_table(
118         remainder_table_name,
119         schema,
120         [remainder_fk_column] + remainder_columns,
121         engine,
122         metadata=extracted_table.metadata
123     )
124     return extracted_table, remainder_table, remainder_fk_column.name
125 
126 
127 def _create_split_insert_stmt(
128         old_table,
129         extracted_table,
130         extracted_columns,
131         remainder_table,
132         remainder_columns,
133         remainder_fk_name,
134 ):
135     SPLIT_ID = "f{constants.MATHESAR_PREFIX}_split_column_alias"
136     extracted_column_names = [col.name for col in extracted_columns]
137     remainder_column_names = [col.name for col in remainder_columns]
138     split_cte = select(
139         [
140             old_table,
141             func.dense_rank().over(order_by=extracted_columns).label(SPLIT_ID)
142         ]
143     ).cte()
144     cte_extraction_columns = (
145         [split_cte.columns[SPLIT_ID]]
146         + [split_cte.columns[n] for n in extracted_column_names]
147     )
148     cte_remainder_columns = (
149         [split_cte.columns[SPLIT_ID]]
150         + [split_cte.columns[n] for n in remainder_column_names]
151     )
152     extract_sel = select(
153         cte_extraction_columns,
154         distinct=True
155     )
156     extract_ins_cte = (
157         extracted_table
158         .insert()
159         .from_select([constants.ID] + extracted_column_names, extract_sel)
160         .returning(literal(1))
161         .cte()
162     )
163     remainder_sel = select(
164         cte_remainder_columns,
165         distinct=True
166     ).where(exists(extract_ins_cte.select()))
167 
168     split_ins = (
169         remainder_table
170         .insert()
171         .from_select(
172             [remainder_fk_name] + remainder_column_names,
173             remainder_sel
174         )
175     )
176     return split_ins
177 
178 
179 def merge_tables(
180         table_name_one,
181         table_name_two,
182         merged_table_name,
183         schema,
184         engine,
185         drop_original_tables=False,
186 ):
187     """
188     This specifically undoes the `extract_columns_from_table` (up to
189     unique rows).  It may not work in other contexts (yet).
190     """
191     table_one = reflect_table(table_name_one, schema, engine)
192     table_two = reflect_table(
193         table_name_two, schema, engine, metadata=table_one.metadata
194     )
195     merge_join = table_one.join(table_two)
196     referencing_columns = [
197         col for col in [merge_join.onclause.left, merge_join.onclause.right]
198         if col.foreign_keys
199     ]
200     merged_columns_all = [
201         columns.MathesarColumn.from_column(col)
202         for col in list(table_one.columns) + list(table_two.columns)
203         if col not in referencing_columns
204     ]
205     merged_columns = [col for col in merged_columns_all if not col.is_default]
206     with engine.begin() as conn:
207         merged_table = create_mathesar_table(
208             merged_table_name, schema, merged_columns, engine,
209         )
210         insert_stmt = merged_table.insert().from_select(
211             [col.name for col in merged_columns],
212             select(merged_columns, distinct=True).select_from(merge_join)
213         )
214         conn.execute(insert_stmt)
215 
216     if drop_original_tables:
217         if table_one.foreign_keys:
218             table_one.drop(bind=engine)
219             table_two.drop(bind=engine)
220         else:
221             table_two.drop(bind=engine)
222             table_one.drop(bind=engine)
223 
224     return merged_table
225 
226 
227 def move_column_between_related_tables(
228         source_table_name,
229         target_table_name,
230         column_names,
231         schema,
232         engine,
233 ):
234     TEMP_MERGED_TABLE_NAME = f"{constants.MATHESAR_PREFIX}_temp_merge_table"
235     source_table = reflect_table(source_table_name, schema, engine)
236     target_table = reflect_table(
237         target_table_name, schema, engine, metadata=source_table.metadata
238     )
239     relationship = _find_table_relationship(source_table, target_table)
240     moving_columns = [source_table.columns[n] for n in column_names]
241     assert _check_columns(relationship, source_table, moving_columns)
242     ext_args = _get_column_moving_extraction_args(
243         relationship,
244         target_table,
245         target_table_name,
246         source_table,
247         source_table_name,
248         moving_columns,
249         column_names,
250     )
251     (extracted_table_name, remainder_table_name, extraction_columns) = ext_args
252     merge_tables(
253         source_table_name,
254         target_table_name,
255         TEMP_MERGED_TABLE_NAME,
256         schema,
257         engine,
258         drop_original_tables=True,
259     )
260     extracted_table, remainder_table, _ = extract_columns_from_table(
261         TEMP_MERGED_TABLE_NAME,
262         [c.name for c in extraction_columns],
263         extracted_table_name,
264         remainder_table_name,
265         schema,
266         engine,
267         drop_original_table=True,
268     )
269     return extracted_table, remainder_table
270 
271 
272 def _find_table_relationship(table_one, table_two):
273     """
274     This function takes two tables, and returns a dict defining the direction
275     of the foreign key constraint relating the tables (if one exists)
276     """
277     one_referencing_two = [
278         fkey_constraint for fkey_constraint in table_one.foreign_key_constraints
279         if fkey_constraint.referred_table == table_two
280     ]
281     two_referencing_one = [
282         fkey_constraint for fkey_constraint in table_two.foreign_key_constraints
283         if fkey_constraint.referred_table == table_one
284     ]
285     if one_referencing_two and not two_referencing_one:
286         relationship = {"referencing": table_one, "referenced": table_two}
287     elif two_referencing_one and not one_referencing_two:
288         relationship = {"referencing": table_two, "referenced": table_one}
289     else:
290         relationship = None
291     return relationship
292 
293 
294 def _check_columns(relationship, source_table, moving_columns):
295     return (
296         relationship is not None
297         and all([not c.foreign_keys for c in moving_columns])
298     )
299 
300 
301 def _get_column_moving_extraction_args(
302         relationship,
303         target_table,
304         target_table_name,
305         source_table,
306         source_table_name,
307         moving_columns,
308         column_names,
309 ):
310     if relationship["referenced"] == target_table:
311         extracted_table_name = target_table_name
312         remainder_table_name = source_table_name
313         extraction_columns = [
314             col for col in target_table.columns
315             if not columns.MathesarColumn.from_column(col).is_default
316         ] + moving_columns
317     else:
318         extracted_table_name = source_table_name
319         remainder_table_name = target_table_name
320         extraction_columns = [
321             col for col in target_table.columns
322             if not columns.MathesarColumn.from_column(col).is_default
323             and col not in column_names
324         ]
325     return extracted_table_name, remainder_table_name, extraction_columns
326 
327 
328 def reflect_table(name, schema, engine, metadata=None):
329     if metadata is None:
330         metadata = MetaData(bind=engine)
331     return Table(name, metadata, schema=schema, autoload_with=engine)
332 
333 
334 def get_count(table, engine):
335     query = select([func.count()]).select_from(table)
336     with engine.begin() as conn:
337         return conn.execute(query).scalar()
338
```
--- 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/tables.py b/db/tables.py
--- a/db/tables.py
+++ b/db/tables.py
@@ -132,7 +132,7 @@
         remainder_columns,
         remainder_fk_name,
 ):
-    SPLIT_ID = "f{constants.MATHESAR_PREFIX}_split_column_alias"
+    SPLIT_ID = f"{constants.MATHESAR_PREFIX}_split_column_alias"
     extracted_column_names = [col.name for col in extracted_columns]
     remainder_column_names = [col.name for col in remainder_columns]
     split_cte = select(
@@ -224,7 +224,7 @@
     return merged_table
 
 
-def move_column_between_related_tables(
+def move_columns_between_related_tables(
         source_table_name,
         target_table_name,
         column_names,
@@ -318,9 +318,9 @@
         extracted_table_name = source_table_name
         remainder_table_name = target_table_name
         extraction_columns = [
-            col for col in target_table.columns
+            col for col in source_table.columns
             if not columns.MathesarColumn.from_column(col).is_default
-            and col not in column_names
+            and col.name not in column_names
         ]
     return extracted_table_name, remainder_table_name, extraction_columns

{"golden_diff": "diff --git a/db/tables.py b/db/tables.py\n--- a/db/tables.py\n+++ b/db/tables.py\n@@ -132,7 +132,7 @@\n         remainder_columns,\n         remainder_fk_name,\n ):\n-    SPLIT_ID = \"f{constants.MATHESAR_PREFIX}_split_column_alias\"\n+    SPLIT_ID = f\"{constants.MATHESAR_PREFIX}_split_column_alias\"\n     extracted_column_names = [col.name for col in extracted_columns]\n     remainder_column_names = [col.name for col in remainder_columns]\n     split_cte = select(\n@@ -224,7 +224,7 @@\n     return merged_table\n \n \n-def move_column_between_related_tables(\n+def move_columns_between_related_tables(\n         source_table_name,\n         target_table_name,\n         column_names,\n@@ -318,9 +318,9 @@\n         extracted_table_name = source_table_name\n         remainder_table_name = target_table_name\n         extraction_columns = [\n-            col for col in target_table.columns\n+            col for col in source_table.columns\n             if not columns.MathesarColumn.from_column(col).is_default\n-            and col not in column_names\n+            and col.name not in column_names\n         ]\n     return extracted_table_name, remainder_table_name, extraction_columns\n", "issue": "Test all code brought in from the prototype\n**Problem**\r\n<!-- Please provide a clear and concise description of the problem that this feature request is designed to solve.-->\r\nThe code brought over from the prototype in PRs #57 , #60, #75, #79, #85 is not yet fully tested\r\n\r\n**Proposed solution**\r\n<!-- A clear and concise description of your proposed solution or feature. -->\r\n\r\nWrite tests covering all code from those PRs which is not yet covered.\r\n\n", "before_files": [{"content": "from sqlalchemy import (\n    Column, String, Table, MetaData, func, select, ForeignKey, literal, exists\n)\n\nfrom db import columns, constants, schemas\n\n\ndef create_string_column_table(name, schema, column_names, engine):\n    \"\"\"\n    This method creates a Postgres table in the specified schema, with all\n    columns being String type.\n    \"\"\"\n    columns_ = [Column(column_name, String) for column_name in column_names]\n    table = create_mathesar_table(name, schema, columns_, engine)\n    return table\n\n\ndef create_mathesar_table(name, schema, columns_, engine, metadata=None):\n    \"\"\"\n    This method creates a Postgres table in the specified schema using the\n    given name and column list.  It adds internal mathesar columns to the\n    table.\n    \"\"\"\n    columns_ = columns.init_mathesar_table_column_list_with_defaults(columns_)\n    schemas.create_schema(schema, engine)\n    # We need this so that we can create multiple mathesar tables in the\n    # same MetaData, enabling them to reference each other in the\n    # SQLAlchemy context (e.g., for creating a ForeignKey relationship)\n    if metadata is None:\n        metadata = MetaData(bind=engine, schema=schema)\n    # This reflection step lets us notice any \"table already exists\"\n    # errors before sending error-generating requests to the DB.\n    metadata.reflect()\n    table = Table(\n        name,\n        metadata,\n        *columns_,\n        schema=schema,\n    )\n    table.create(engine)\n    return table\n\n\ndef extract_columns_from_table(\n        old_table_name,\n        extracted_column_names,\n        extracted_table_name,\n        remainder_table_name,\n        schema,\n        engine,\n        drop_original_table=False,\n):\n    old_table = reflect_table(old_table_name, schema, engine)\n    old_columns = (\n        columns.MathesarColumn.from_column(col) for col in old_table.columns\n    )\n    old_non_default_columns = [\n        col for col in old_columns if not col.is_default\n    ]\n    extracted_columns, remainder_columns = _split_column_list(\n        old_non_default_columns, extracted_column_names,\n    )\n    with engine.begin() as conn:\n        extracted_table, remainder_table, remainder_fk = _create_split_tables(\n            extracted_table_name,\n            extracted_columns,\n            remainder_table_name,\n            remainder_columns,\n            schema,\n            engine,\n        )\n        split_ins = _create_split_insert_stmt(\n            old_table,\n            extracted_table,\n            extracted_columns,\n            remainder_table,\n            remainder_columns,\n            remainder_fk,\n        )\n        conn.execute(split_ins)\n    if drop_original_table:\n        old_table.drop()\n\n    return extracted_table, remainder_table, remainder_fk\n\n\ndef _split_column_list(columns_, extracted_column_names):\n    extracted_columns = [\n        col for col in columns_ if col.name in extracted_column_names\n    ]\n    remainder_columns = [\n        col for col in columns_ if col.name not in extracted_column_names\n    ]\n    return extracted_columns, remainder_columns\n\n\ndef _create_split_tables(\n        extracted_table_name,\n        extracted_columns,\n        remainder_table_name,\n        remainder_columns,\n        schema,\n        engine,\n):\n    extracted_table = create_mathesar_table(\n        extracted_table_name,\n        schema,\n        extracted_columns,\n        engine,\n    )\n    remainder_fk_column = Column(\n        f\"{extracted_table.name}_{constants.ID}\",\n        columns.ID_TYPE,\n        ForeignKey(f\"{extracted_table.name}.{constants.ID}\"),\n        nullable=False,\n    )\n    remainder_table = create_mathesar_table(\n        remainder_table_name,\n        schema,\n        [remainder_fk_column] + remainder_columns,\n        engine,\n        metadata=extracted_table.metadata\n    )\n    return extracted_table, remainder_table, remainder_fk_column.name\n\n\ndef _create_split_insert_stmt(\n        old_table,\n        extracted_table,\n        extracted_columns,\n        remainder_table,\n        remainder_columns,\n        remainder_fk_name,\n):\n    SPLIT_ID = \"f{constants.MATHESAR_PREFIX}_split_column_alias\"\n    extracted_column_names = [col.name for col in extracted_columns]\n    remainder_column_names = [col.name for col in remainder_columns]\n    split_cte = select(\n        [\n            old_table,\n            func.dense_rank().over(order_by=extracted_columns).label(SPLIT_ID)\n        ]\n    ).cte()\n    cte_extraction_columns = (\n        [split_cte.columns[SPLIT_ID]]\n        + [split_cte.columns[n] for n in extracted_column_names]\n    )\n    cte_remainder_columns = (\n        [split_cte.columns[SPLIT_ID]]\n        + [split_cte.columns[n] for n in remainder_column_names]\n    )\n    extract_sel = select(\n        cte_extraction_columns,\n        distinct=True\n    )\n    extract_ins_cte = (\n        extracted_table\n        .insert()\n        .from_select([constants.ID] + extracted_column_names, extract_sel)\n        .returning(literal(1))\n        .cte()\n    )\n    remainder_sel = select(\n        cte_remainder_columns,\n        distinct=True\n    ).where(exists(extract_ins_cte.select()))\n\n    split_ins = (\n        remainder_table\n        .insert()\n        .from_select(\n            [remainder_fk_name] + remainder_column_names,\n            remainder_sel\n        )\n    )\n    return split_ins\n\n\ndef merge_tables(\n        table_name_one,\n        table_name_two,\n        merged_table_name,\n        schema,\n        engine,\n        drop_original_tables=False,\n):\n    \"\"\"\n    This specifically undoes the `extract_columns_from_table` (up to\n    unique rows).  It may not work in other contexts (yet).\n    \"\"\"\n    table_one = reflect_table(table_name_one, schema, engine)\n    table_two = reflect_table(\n        table_name_two, schema, engine, metadata=table_one.metadata\n    )\n    merge_join = table_one.join(table_two)\n    referencing_columns = [\n        col for col in [merge_join.onclause.left, merge_join.onclause.right]\n        if col.foreign_keys\n    ]\n    merged_columns_all = [\n        columns.MathesarColumn.from_column(col)\n        for col in list(table_one.columns) + list(table_two.columns)\n        if col not in referencing_columns\n    ]\n    merged_columns = [col for col in merged_columns_all if not col.is_default]\n    with engine.begin() as conn:\n        merged_table = create_mathesar_table(\n            merged_table_name, schema, merged_columns, engine,\n        )\n        insert_stmt = merged_table.insert().from_select(\n            [col.name for col in merged_columns],\n            select(merged_columns, distinct=True).select_from(merge_join)\n        )\n        conn.execute(insert_stmt)\n\n    if drop_original_tables:\n        if table_one.foreign_keys:\n            table_one.drop(bind=engine)\n            table_two.drop(bind=engine)\n        else:\n            table_two.drop(bind=engine)\n            table_one.drop(bind=engine)\n\n    return merged_table\n\n\ndef move_column_between_related_tables(\n        source_table_name,\n        target_table_name,\n        column_names,\n        schema,\n        engine,\n):\n    TEMP_MERGED_TABLE_NAME = f\"{constants.MATHESAR_PREFIX}_temp_merge_table\"\n    source_table = reflect_table(source_table_name, schema, engine)\n    target_table = reflect_table(\n        target_table_name, schema, engine, metadata=source_table.metadata\n    )\n    relationship = _find_table_relationship(source_table, target_table)\n    moving_columns = [source_table.columns[n] for n in column_names]\n    assert _check_columns(relationship, source_table, moving_columns)\n    ext_args = _get_column_moving_extraction_args(\n        relationship,\n        target_table,\n        target_table_name,\n        source_table,\n        source_table_name,\n        moving_columns,\n        column_names,\n    )\n    (extracted_table_name, remainder_table_name, extraction_columns) = ext_args\n    merge_tables(\n        source_table_name,\n        target_table_name,\n        TEMP_MERGED_TABLE_NAME,\n        schema,\n        engine,\n        drop_original_tables=True,\n    )\n    extracted_table, remainder_table, _ = extract_columns_from_table(\n        TEMP_MERGED_TABLE_NAME,\n        [c.name for c in extraction_columns],\n        extracted_table_name,\n        remainder_table_name,\n        schema,\n        engine,\n        drop_original_table=True,\n    )\n    return extracted_table, remainder_table\n\n\ndef _find_table_relationship(table_one, table_two):\n    \"\"\"\n    This function takes two tables, and returns a dict defining the direction\n    of the foreign key constraint relating the tables (if one exists)\n    \"\"\"\n    one_referencing_two = [\n        fkey_constraint for fkey_constraint in table_one.foreign_key_constraints\n        if fkey_constraint.referred_table == table_two\n    ]\n    two_referencing_one = [\n        fkey_constraint for fkey_constraint in table_two.foreign_key_constraints\n        if fkey_constraint.referred_table == table_one\n    ]\n    if one_referencing_two and not two_referencing_one:\n        relationship = {\"referencing\": table_one, \"referenced\": table_two}\n    elif two_referencing_one and not one_referencing_two:\n        relationship = {\"referencing\": table_two, \"referenced\": table_one}\n    else:\n        relationship = None\n    return relationship\n\n\ndef _check_columns(relationship, source_table, moving_columns):\n    return (\n        relationship is not None\n        and all([not c.foreign_keys for c in moving_columns])\n    )\n\n\ndef _get_column_moving_extraction_args(\n        relationship,\n        target_table,\n        target_table_name,\n        source_table,\n        source_table_name,\n        moving_columns,\n        column_names,\n):\n    if relationship[\"referenced\"] == target_table:\n        extracted_table_name = target_table_name\n        remainder_table_name = source_table_name\n        extraction_columns = [\n            col for col in target_table.columns\n            if not columns.MathesarColumn.from_column(col).is_default\n        ] + moving_columns\n    else:\n        extracted_table_name = source_table_name\n        remainder_table_name = target_table_name\n        extraction_columns = [\n            col for col in target_table.columns\n            if not columns.MathesarColumn.from_column(col).is_default\n            and col not in column_names\n        ]\n    return extracted_table_name, remainder_table_name, extraction_columns\n\n\ndef reflect_table(name, schema, engine, metadata=None):\n    if metadata is None:\n        metadata = MetaData(bind=engine)\n    return Table(name, metadata, schema=schema, autoload_with=engine)\n\n\ndef get_count(table, engine):\n    query = select([func.count()]).select_from(table)\n    with engine.begin() as conn:\n        return conn.execute(query).scalar()\n", "path": "db/tables.py"}], "after_files": [{"content": "from sqlalchemy import (\n    Column, String, Table, MetaData, func, select, ForeignKey, literal, exists\n)\n\nfrom db import columns, constants, schemas\n\n\ndef create_string_column_table(name, schema, column_names, engine):\n    \"\"\"\n    This method creates a Postgres table in the specified schema, with all\n    columns being String type.\n    \"\"\"\n    columns_ = [Column(column_name, String) for column_name in column_names]\n    table = create_mathesar_table(name, schema, columns_, engine)\n    return table\n\n\ndef create_mathesar_table(name, schema, columns_, engine, metadata=None):\n    \"\"\"\n    This method creates a Postgres table in the specified schema using the\n    given name and column list.  It adds internal mathesar columns to the\n    table.\n    \"\"\"\n    columns_ = columns.init_mathesar_table_column_list_with_defaults(columns_)\n    schemas.create_schema(schema, engine)\n    # We need this so that we can create multiple mathesar tables in the\n    # same MetaData, enabling them to reference each other in the\n    # SQLAlchemy context (e.g., for creating a ForeignKey relationship)\n    if metadata is None:\n        metadata = MetaData(bind=engine, schema=schema)\n    # This reflection step lets us notice any \"table already exists\"\n    # errors before sending error-generating requests to the DB.\n    metadata.reflect()\n    table = Table(\n        name,\n        metadata,\n        *columns_,\n        schema=schema,\n    )\n    table.create(engine)\n    return table\n\n\ndef extract_columns_from_table(\n        old_table_name,\n        extracted_column_names,\n        extracted_table_name,\n        remainder_table_name,\n        schema,\n        engine,\n        drop_original_table=False,\n):\n    old_table = reflect_table(old_table_name, schema, engine)\n    old_columns = (\n        columns.MathesarColumn.from_column(col) for col in old_table.columns\n    )\n    old_non_default_columns = [\n        col for col in old_columns if not col.is_default\n    ]\n    extracted_columns, remainder_columns = _split_column_list(\n        old_non_default_columns, extracted_column_names,\n    )\n    with engine.begin() as conn:\n        extracted_table, remainder_table, remainder_fk = _create_split_tables(\n            extracted_table_name,\n            extracted_columns,\n            remainder_table_name,\n            remainder_columns,\n            schema,\n            engine,\n        )\n        split_ins = _create_split_insert_stmt(\n            old_table,\n            extracted_table,\n            extracted_columns,\n            remainder_table,\n            remainder_columns,\n            remainder_fk,\n        )\n        conn.execute(split_ins)\n    if drop_original_table:\n        old_table.drop()\n\n    return extracted_table, remainder_table, remainder_fk\n\n\ndef _split_column_list(columns_, extracted_column_names):\n    extracted_columns = [\n        col for col in columns_ if col.name in extracted_column_names\n    ]\n    remainder_columns = [\n        col for col in columns_ if col.name not in extracted_column_names\n    ]\n    return extracted_columns, remainder_columns\n\n\ndef _create_split_tables(\n        extracted_table_name,\n        extracted_columns,\n        remainder_table_name,\n        remainder_columns,\n        schema,\n        engine,\n):\n    extracted_table = create_mathesar_table(\n        extracted_table_name,\n        schema,\n        extracted_columns,\n        engine,\n    )\n    remainder_fk_column = Column(\n        f\"{extracted_table.name}_{constants.ID}\",\n        columns.ID_TYPE,\n        ForeignKey(f\"{extracted_table.name}.{constants.ID}\"),\n        nullable=False,\n    )\n    remainder_table = create_mathesar_table(\n        remainder_table_name,\n        schema,\n        [remainder_fk_column] + remainder_columns,\n        engine,\n        metadata=extracted_table.metadata\n    )\n    return extracted_table, remainder_table, remainder_fk_column.name\n\n\ndef _create_split_insert_stmt(\n        old_table,\n        extracted_table,\n        extracted_columns,\n        remainder_table,\n        remainder_columns,\n        remainder_fk_name,\n):\n    SPLIT_ID = f\"{constants.MATHESAR_PREFIX}_split_column_alias\"\n    extracted_column_names = [col.name for col in extracted_columns]\n    remainder_column_names = [col.name for col in remainder_columns]\n    split_cte = select(\n        [\n            old_table,\n            func.dense_rank().over(order_by=extracted_columns).label(SPLIT_ID)\n        ]\n    ).cte()\n    cte_extraction_columns = (\n        [split_cte.columns[SPLIT_ID]]\n        + [split_cte.columns[n] for n in extracted_column_names]\n    )\n    cte_remainder_columns = (\n        [split_cte.columns[SPLIT_ID]]\n        + [split_cte.columns[n] for n in remainder_column_names]\n    )\n    extract_sel = select(\n        cte_extraction_columns,\n        distinct=True\n    )\n    extract_ins_cte = (\n        extracted_table\n        .insert()\n        .from_select([constants.ID] + extracted_column_names, extract_sel)\n        .returning(literal(1))\n        .cte()\n    )\n    remainder_sel = select(\n        cte_remainder_columns,\n        distinct=True\n    ).where(exists(extract_ins_cte.select()))\n\n    split_ins = (\n        remainder_table\n        .insert()\n        .from_select(\n            [remainder_fk_name] + remainder_column_names,\n            remainder_sel\n        )\n    )\n    return split_ins\n\n\ndef merge_tables(\n        table_name_one,\n        table_name_two,\n        merged_table_name,\n        schema,\n        engine,\n        drop_original_tables=False,\n):\n    \"\"\"\n    This specifically undoes the `extract_columns_from_table` (up to\n    unique rows).  It may not work in other contexts (yet).\n    \"\"\"\n    table_one = reflect_table(table_name_one, schema, engine)\n    table_two = reflect_table(\n        table_name_two, schema, engine, metadata=table_one.metadata\n    )\n    merge_join = table_one.join(table_two)\n    referencing_columns = [\n        col for col in [merge_join.onclause.left, merge_join.onclause.right]\n        if col.foreign_keys\n    ]\n    merged_columns_all = [\n        columns.MathesarColumn.from_column(col)\n        for col in list(table_one.columns) + list(table_two.columns)\n        if col not in referencing_columns\n    ]\n    merged_columns = [col for col in merged_columns_all if not col.is_default]\n    with engine.begin() as conn:\n        merged_table = create_mathesar_table(\n            merged_table_name, schema, merged_columns, engine,\n        )\n        insert_stmt = merged_table.insert().from_select(\n            [col.name for col in merged_columns],\n            select(merged_columns, distinct=True).select_from(merge_join)\n        )\n        conn.execute(insert_stmt)\n\n    if drop_original_tables:\n        if table_one.foreign_keys:\n            table_one.drop(bind=engine)\n            table_two.drop(bind=engine)\n        else:\n            table_two.drop(bind=engine)\n            table_one.drop(bind=engine)\n\n    return merged_table\n\n\ndef move_columns_between_related_tables(\n        source_table_name,\n        target_table_name,\n        column_names,\n        schema,\n        engine,\n):\n    TEMP_MERGED_TABLE_NAME = f\"{constants.MATHESAR_PREFIX}_temp_merge_table\"\n    source_table = reflect_table(source_table_name, schema, engine)\n    target_table = reflect_table(\n        target_table_name, schema, engine, metadata=source_table.metadata\n    )\n    relationship = _find_table_relationship(source_table, target_table)\n    moving_columns = [source_table.columns[n] for n in column_names]\n    assert _check_columns(relationship, source_table, moving_columns)\n    ext_args = _get_column_moving_extraction_args(\n        relationship,\n        target_table,\n        target_table_name,\n        source_table,\n        source_table_name,\n        moving_columns,\n        column_names,\n    )\n    (extracted_table_name, remainder_table_name, extraction_columns) = ext_args\n    merge_tables(\n        source_table_name,\n        target_table_name,\n        TEMP_MERGED_TABLE_NAME,\n        schema,\n        engine,\n        drop_original_tables=True,\n    )\n    extracted_table, remainder_table, _ = extract_columns_from_table(\n        TEMP_MERGED_TABLE_NAME,\n        [c.name for c in extraction_columns],\n        extracted_table_name,\n        remainder_table_name,\n        schema,\n        engine,\n        drop_original_table=True,\n    )\n    return extracted_table, remainder_table\n\n\ndef _find_table_relationship(table_one, table_two):\n    \"\"\"\n    This function takes two tables, and returns a dict defining the direction\n    of the foreign key constraint relating the tables (if one exists)\n    \"\"\"\n    one_referencing_two = [\n        fkey_constraint for fkey_constraint in table_one.foreign_key_constraints\n        if fkey_constraint.referred_table == table_two\n    ]\n    two_referencing_one = [\n        fkey_constraint for fkey_constraint in table_two.foreign_key_constraints\n        if fkey_constraint.referred_table == table_one\n    ]\n    if one_referencing_two and not two_referencing_one:\n        relationship = {\"referencing\": table_one, \"referenced\": table_two}\n    elif two_referencing_one and not one_referencing_two:\n        relationship = {\"referencing\": table_two, \"referenced\": table_one}\n    else:\n        relationship = None\n    return relationship\n\n\ndef _check_columns(relationship, source_table, moving_columns):\n    return (\n        relationship is not None\n        and all([not c.foreign_keys for c in moving_columns])\n    )\n\n\ndef _get_column_moving_extraction_args(\n        relationship,\n        target_table,\n        target_table_name,\n        source_table,\n        source_table_name,\n        moving_columns,\n        column_names,\n):\n    if relationship[\"referenced\"] == target_table:\n        extracted_table_name = target_table_name\n        remainder_table_name = source_table_name\n        extraction_columns = [\n            col for col in target_table.columns\n            if not columns.MathesarColumn.from_column(col).is_default\n        ] + moving_columns\n    else:\n        extracted_table_name = source_table_name\n        remainder_table_name = target_table_name\n        extraction_columns = [\n            col for col in source_table.columns\n            if not columns.MathesarColumn.from_column(col).is_default\n            and col.name not in column_names\n        ]\n    return extracted_table_name, remainder_table_name, extraction_columns\n\n\ndef reflect_table(name, schema, engine, metadata=None):\n    if metadata is None:\n        metadata = MetaData(bind=engine)\n    return Table(name, metadata, schema=schema, autoload_with=engine)\n\n\ndef get_count(table, engine):\n    query = select([func.count()]).select_from(table)\n    with engine.begin() as conn:\n        return conn.execute(query).scalar()\n", "path": "db/tables.py"}]}