Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/cli-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ See :ref:`cli_transform_table`.
Add a foreign key constraint from a column to
another table with another column
--drop-foreign-key TEXT Drop foreign key constraint for this column
--update-incoming-fks Update foreign keys in other tables that
reference renamed columns
--sql Output SQL without executing it
--load-extension TEXT Path to SQLite extension, with optional
:entrypoint
Expand Down
3 changes: 3 additions & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2113,6 +2113,9 @@ Every option for this table (with the exception of ``--pk-none``) can be specifi
``--add-foreign-key column other_table other_column``
Add a foreign key constraint to ``column`` pointing to ``other_table.other_column``.

``--update-incoming-fks``
When renaming columns, automatically update foreign key constraints in other tables that reference the renamed columns. For example, if ``books.author_id`` references ``authors.id`` and you rename ``authors.id`` to ``authors.author_pk``, this flag will also update the foreign key in ``books`` to reference the new column name.

If you want to see the SQL that will be executed to make the change without actually executing it, add the ``--sql`` flag. For example:

.. code-block:: bash
Expand Down
25 changes: 25 additions & 0 deletions docs/python-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,31 @@ This example drops two foreign keys - the one from ``places.country`` to ``count
drop_foreign_keys=("country", "continent")
)

.. _python_api_transform_update_incoming_fks:

Updating foreign keys in other tables
-------------------------------------

When renaming columns that are referenced by foreign keys in other tables, you can use the ``update_incoming_fks=True`` parameter to automatically update those foreign key constraints.

For example, if you have a ``books`` table with a foreign key from ``books.author_id`` to ``authors.id``, and you want to rename ``authors.id`` to ``authors.author_pk``:

.. code-block:: python

db["authors"].transform(
rename={"id": "author_pk"},
update_incoming_fks=True,
)

This will rename the column in the ``authors`` table and also update the foreign key constraint in the ``books`` table to reference ``authors.author_pk`` instead of ``authors.id``.

Without ``update_incoming_fks=True``, this operation would fail with a foreign key mismatch error (if foreign key enforcement is enabled) because the ``books`` table would still reference the old column name.

This parameter also correctly handles:

- Multiple tables referencing the renamed column
- Self-referential foreign keys (e.g., an ``employees.manager_id`` column referencing ``employees.id``)

.. _python_api_transform_sql:

Custom transformations with .transform_sql()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ dependencies = [

[dependency-groups]
dev = [
"black>=24.1.1",
"black>=26.1.0",
"cogapp",
"hypothesis",
"pytest",
Expand Down
27 changes: 14 additions & 13 deletions sqlite_utils/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
TypeTracker,
)


CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])


Expand Down Expand Up @@ -2545,6 +2544,11 @@ def schema(
multiple=True,
help="Drop foreign key constraint for this column",
)
@click.option(
"--update-incoming-fks",
is_flag=True,
help="Update foreign keys in other tables that reference renamed columns",
)
@click.option("--sql", is_flag=True, help="Output SQL without executing it")
@load_extension_option
def transform(
Expand All @@ -2562,6 +2566,7 @@ def transform(
default_none,
add_foreign_keys,
drop_foreign_keys,
update_incoming_fks,
sql,
load_extension,
):
Expand Down Expand Up @@ -2615,6 +2620,8 @@ def transform(
kwargs["drop_foreign_keys"] = drop_foreign_keys
if add_foreign_keys:
kwargs["add_foreign_keys"] = add_foreign_keys
if update_incoming_fks:
kwargs["update_incoming_fks"] = True

if sql:
for line in db.table(table).transform_sql(**kwargs):
Expand Down Expand Up @@ -2911,17 +2918,15 @@ def _analyze(db, tables, columns, save, common_limit=10, no_most=False, no_least
)
details = (
(
textwrap.dedent(
"""
textwrap.dedent("""
{table}.{column}: ({i}/{total})

Total rows: {total_rows}
Null rows: {num_null}
Blank rows: {num_blank}

Distinct values: {num_distinct}{most_common_rendered}{least_common_rendered}
"""
)
""")
.strip()
.format(
i=i + 1,
Expand Down Expand Up @@ -2968,8 +2973,7 @@ def uninstall(packages, yes):


def _generate_convert_help():
help = textwrap.dedent(
"""
help = textwrap.dedent("""
Convert columns using Python code you supply. For example:

\b
Expand All @@ -2982,8 +2986,7 @@ def _generate_convert_help():
Use "-" for CODE to read Python code from standard input.

The following common operations are available as recipe functions:
"""
).strip()
""").strip()
recipe_names = [
n
for n in dir(recipes)
Expand All @@ -2997,15 +3000,13 @@ def _generate_convert_help():
name, str(inspect.signature(fn)), textwrap.dedent(fn.__doc__.rstrip())
)
help += "\n\n"
help += textwrap.dedent(
"""
help += textwrap.dedent("""
You can use these recipes like so:

\b
sqlite-utils convert my.db mytable mycolumn \\
'r.jsonsplit(value, delimiter=":")'
"""
).strip()
""").strip()
return help


Expand Down
Loading
Loading