mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-10 10:34:10 +02:00
create_index(..., find_unique_name=True) option, refs #335
This commit is contained in:
parent
92aa5c9c5d
commit
e8d958109e
3 changed files with 62 additions and 18 deletions
|
|
@ -2141,7 +2141,9 @@ You can create an index on a table using the ``.create_index(columns)`` method.
|
||||||
|
|
||||||
db["dogs"].create_index(["is_good_dog"])
|
db["dogs"].create_index(["is_good_dog"])
|
||||||
|
|
||||||
By default the index will be named ``idx_{table-name}_{columns}`` - if you want to customize the name of the created index you can pass the ``index_name`` parameter:
|
By default the index will be named ``idx_{table-name}_{columns}``. If you pass ``find_unique_name=True`` and the automatically derived name already exists, an available name will be found by incrementing a suffix number, for example ``idx_items_title_2``.
|
||||||
|
|
||||||
|
You can customize the name of the created index by passing the ``index_name`` parameter:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -899,7 +899,7 @@ class Database:
|
||||||
}
|
}
|
||||||
for fk in table.foreign_keys:
|
for fk in table.foreign_keys:
|
||||||
if fk.column not in existing_indexes:
|
if fk.column not in existing_indexes:
|
||||||
table.create_index([fk.column])
|
table.create_index([fk.column], find_unique_name=True)
|
||||||
|
|
||||||
def vacuum(self):
|
def vacuum(self):
|
||||||
"Run a SQLite ``VACUUM`` against the database."
|
"Run a SQLite ``VACUUM`` against the database."
|
||||||
|
|
@ -1521,6 +1521,7 @@ class Table(Queryable):
|
||||||
index_name: Optional[str] = None,
|
index_name: Optional[str] = None,
|
||||||
unique: bool = False,
|
unique: bool = False,
|
||||||
if_not_exists: bool = False,
|
if_not_exists: bool = False,
|
||||||
|
find_unique_name: bool = False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Create an index on this table.
|
Create an index on this table.
|
||||||
|
|
@ -1530,6 +1531,8 @@ class Table(Queryable):
|
||||||
- ``index_name`` - the name to use for the new index. Defaults to the column names joined on ``_``.
|
- ``index_name`` - the name to use for the new index. Defaults to the column names joined on ``_``.
|
||||||
- ``unique`` - should the index be marked as unique, forcing unique values?
|
- ``unique`` - should the index be marked as unique, forcing unique values?
|
||||||
- ``if_not_exists`` - only create the index if one with that name does not already exist.
|
- ``if_not_exists`` - only create the index if one with that name does not already exist.
|
||||||
|
- ``find_unique_name`` - if ``index_name`` is not provided and the automatically derived name
|
||||||
|
already exists, keep incrementing a suffix number to find an available name.
|
||||||
|
|
||||||
See :ref:`python_api_create_index`.
|
See :ref:`python_api_create_index`.
|
||||||
"""
|
"""
|
||||||
|
|
@ -1544,23 +1547,45 @@ class Table(Queryable):
|
||||||
else:
|
else:
|
||||||
fmt = "[{}]"
|
fmt = "[{}]"
|
||||||
columns_sql.append(fmt.format(column))
|
columns_sql.append(fmt.format(column))
|
||||||
sql = (
|
|
||||||
textwrap.dedent(
|
suffix = None
|
||||||
"""
|
while True:
|
||||||
CREATE {unique}INDEX {if_not_exists}[{index_name}]
|
sql = (
|
||||||
ON [{table_name}] ({columns});
|
textwrap.dedent(
|
||||||
"""
|
"""
|
||||||
|
CREATE {unique}INDEX {if_not_exists}[{index_name}]
|
||||||
|
ON [{table_name}] ({columns});
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
.strip()
|
||||||
|
.format(
|
||||||
|
index_name="{}_{}".format(index_name, suffix)
|
||||||
|
if suffix
|
||||||
|
else index_name,
|
||||||
|
table_name=self.name,
|
||||||
|
columns=", ".join(columns_sql),
|
||||||
|
unique="UNIQUE " if unique else "",
|
||||||
|
if_not_exists="IF NOT EXISTS " if if_not_exists else "",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.strip()
|
try:
|
||||||
.format(
|
self.db.execute(sql)
|
||||||
index_name=index_name,
|
break
|
||||||
table_name=self.name,
|
except OperationalError as e:
|
||||||
columns=", ".join(columns_sql),
|
# find_unique_name=True - try again if 'index ... already exists'
|
||||||
unique="UNIQUE " if unique else "",
|
arg = e.args[0]
|
||||||
if_not_exists="IF NOT EXISTS " if if_not_exists else "",
|
if (
|
||||||
)
|
find_unique_name
|
||||||
)
|
and arg.startswith("index ")
|
||||||
self.db.execute(sql)
|
and arg.endswith(" already exists")
|
||||||
|
):
|
||||||
|
if suffix is None:
|
||||||
|
suffix = 2
|
||||||
|
else:
|
||||||
|
suffix += 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_column(
|
def add_column(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from sqlite_utils.db import (
|
||||||
DescIndex,
|
DescIndex,
|
||||||
AlterError,
|
AlterError,
|
||||||
NoObviousTable,
|
NoObviousTable,
|
||||||
|
OperationalError,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
Table,
|
Table,
|
||||||
View,
|
View,
|
||||||
|
|
@ -752,6 +753,22 @@ def test_create_index_desc(fresh_db):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_index_find_unique_name():
|
||||||
|
db = Database(memory=True)
|
||||||
|
table = db["t"]
|
||||||
|
table.insert({"id": 1})
|
||||||
|
table.create_index(["id"])
|
||||||
|
# Without find_unique_name should error
|
||||||
|
with pytest.raises(OperationalError, match="index idx_t_id already exists"):
|
||||||
|
table.create_index(["id"])
|
||||||
|
# With find_unique_name=True it should work
|
||||||
|
table.create_index(["id"], find_unique_name=True)
|
||||||
|
table.create_index(["id"], find_unique_name=True)
|
||||||
|
# Should have three now
|
||||||
|
index_names = {idx.name for idx in table.indexes}
|
||||||
|
assert index_names == {"idx_t_id", "idx_t_id_2", "idx_t_id_3"}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"data_structure",
|
"data_structure",
|
||||||
(
|
(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue