Skip to content

Commit

Permalink
Added parameters if_exists and if_not_exists for index operations.
Browse files Browse the repository at this point in the history
Fixes: #151

<!-- Provide a general summary of your proposed changes in the Title field above -->

### Description
<!-- Describe your changes in detail -->

As mentioned in #151, IF EXISTS/IF NOT EXISTS syntax was implemented in SQLAlchemy 2.0. This request adds an ability to use them for index operations.

If the issue implies to implement all the possible cases with these directives, I could continue working on it 🙂

### Checklist
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)

-->

This pull request is:

- [ ] A documentation / typographical error fix
	- Good to go, no issue or tests are needed
- [ ] A short code fix
	- please include the issue number, and create an issue if none exists, which
	  must include a complete example of the issue.  one line code fixes without an
	  issue and demonstration will not be accepted.
	- Please include: `Fixes: #<issue number>` in the commit message
	- please include tests.   one line code fixes without tests will not be accepted.
- [x] A new feature implementation
	- please include the issue number, and create an issue if none exists, which must
	  include a complete example of how the feature would look.
	- Please include: `Fixes: #<issue number>` in the commit message
	- please include tests.

**Have a nice day!**

Closes: #1260
Pull-request: #1260
Pull-request-sha: 5ed62d1

Change-Id: Ic0fec21e20d4a868e9e29275e540f3e09918d1ff
  • Loading branch information
ionsome authored and CaselIT committed Jul 11, 2023
1 parent f5e7fd6 commit 88db76e
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 68 deletions.
8 changes: 4 additions & 4 deletions alembic/ddl/impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,8 @@ def drop_table(self, table: Table) -> None:
table, self.connection, checkfirst=False, _ddl_runner=self
)

def create_index(self, index: Index) -> None:
self._exec(schema.CreateIndex(index))
def create_index(self, index: Index, **kw: Any) -> None:
self._exec(schema.CreateIndex(index, **kw))

def create_table_comment(self, table: Table) -> None:
self._exec(schema.SetTableComment(table))
Expand All @@ -391,8 +391,8 @@ def drop_table_comment(self, table: Table) -> None:
def create_column_comment(self, column: ColumnElement[Any]) -> None:
self._exec(schema.SetColumnComment(column))

def drop_index(self, index: Index) -> None:
self._exec(schema.DropIndex(index))
def drop_index(self, index: Index, **kw: Any) -> None:
self._exec(schema.DropIndex(index, **kw))

def bulk_insert(
self,
Expand Down
4 changes: 2 additions & 2 deletions alembic/ddl/mssql.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def alter_column( # type:ignore[override]
table_name, column_name, schema=schema, name=name
)

def create_index(self, index: Index) -> None:
def create_index(self, index: Index, **kw: Any) -> None:
# this likely defaults to None if not present, so get()
# should normally not return the default value. being
# defensive in any case
Expand All @@ -179,7 +179,7 @@ def create_index(self, index: Index) -> None:
for col in mssql_include:
if col not in index.table.c:
index.table.append_column(Column(col, sqltypes.NullType))
self._exec(CreateIndex(index))
self._exec(CreateIndex(index, **kw))

def bulk_insert( # type:ignore[override]
self, table: Union[TableClause, Table], rows: List[dict], **kw: Any
Expand Down
10 changes: 6 additions & 4 deletions alembic/ddl/postgresql.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,17 @@ class PostgresqlImpl(DefaultImpl):
)
identity_attrs_ignore = ("on_null", "order")

def create_index(self, index):
def create_index(self, index: Index, **kw: Any) -> None:
# this likely defaults to None if not present, so get()
# should normally not return the default value. being
# defensive in any case
postgresql_include = index.kwargs.get("postgresql_include", None) or ()
for col in postgresql_include:
if col not in index.table.c:
index.table.append_column(Column(col, sqltypes.NullType))
self._exec(CreateIndex(index))
if col not in index.table.c: # type: ignore[union-attr]
index.table.append_column( # type: ignore[union-attr]
Column(col, sqltypes.NullType)
)
self._exec(CreateIndex(index, **kw))

def prep_table_for_batch(self, batch_impl, table):
for constraint in table.constraints:
Expand Down
46 changes: 29 additions & 17 deletions alembic/op.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ def create_index(
*,
schema: Optional[str] = None,
unique: bool = False,
if_not_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "create index" instruction using the current
Expand Down Expand Up @@ -675,20 +676,24 @@ def create_index(
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param unique: If True, create a unique index.
:param quote:
Force quoting of this column's name on or off, corresponding
to ``True`` or ``False``. When left at its default
of ``None``, the column identifier will be quoted according to
whether the name is case sensitive (identifiers with at least one
upper case character are treated as case sensitive), or if it's a
reserved word. This flag is only needed to force quoting of a
reserved word which is not known by the SQLAlchemy dialect.
:param quote: Force quoting of this column's name on or off,
corresponding to ``True`` or ``False``. When left at its default
of ``None``, the column identifier will be quoted according to
whether the name is case sensitive (identifiers with at least one
upper case character are treated as case sensitive), or if it's a
reserved word. This flag is only needed to force quoting of a
reserved word which is not known by the SQLAlchemy dialect.
:param if_not_exists: If True, adds IF NOT EXISTS operator when
creating the new index.
.. versionadded:: 1.12.0
:param \**kw: Additional keyword arguments not mentioned above are
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
"""

Expand Down Expand Up @@ -955,6 +960,7 @@ def drop_index(
table_name: Optional[str] = None,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "drop index" instruction using the current
Expand All @@ -971,11 +977,17 @@ def drop_index(
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the index.
.. versionadded:: 1.12.0
:param \**kw: Additional keyword arguments not mentioned above are
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
"""

Expand Down Expand Up @@ -1228,7 +1240,7 @@ def invoke(operation: MigrateOperation) -> Any:

def register_operation(
name: str, sourcename: Optional[str] = None
) -> Callable[..., Any]:
) -> Callable[[_T], _T]:
"""Register a new operation for this class.
This method is normally used to add new operations
Expand Down
44 changes: 28 additions & 16 deletions alembic/operations/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,7 @@ def create_index(
*,
schema: Optional[str] = None,
unique: bool = False,
if_not_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "create index" instruction using the current
Expand Down Expand Up @@ -1064,20 +1065,24 @@ def create_index(
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param unique: If True, create a unique index.
:param quote:
Force quoting of this column's name on or off, corresponding
to ``True`` or ``False``. When left at its default
of ``None``, the column identifier will be quoted according to
whether the name is case sensitive (identifiers with at least one
upper case character are treated as case sensitive), or if it's a
reserved word. This flag is only needed to force quoting of a
reserved word which is not known by the SQLAlchemy dialect.
:param quote: Force quoting of this column's name on or off,
corresponding to ``True`` or ``False``. When left at its default
of ``None``, the column identifier will be quoted according to
whether the name is case sensitive (identifiers with at least one
upper case character are treated as case sensitive), or if it's a
reserved word. This flag is only needed to force quoting of a
reserved word which is not known by the SQLAlchemy dialect.
:param if_not_exists: If True, adds IF NOT EXISTS operator when
creating the new index.
.. versionadded:: 1.12.0
:param \**kw: Additional keyword arguments not mentioned above are
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
""" # noqa: E501
...
Expand Down Expand Up @@ -1359,6 +1364,7 @@ def drop_index(
table_name: Optional[str] = None,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "drop index" instruction using the current
Expand All @@ -1375,11 +1381,17 @@ def drop_index(
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the index.
.. versionadded:: 1.12.0
:param \**kw: Additional keyword arguments not mentioned above are
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
""" # noqa: E501
...
Expand Down
8 changes: 4 additions & 4 deletions alembic/operations/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,11 @@ def drop_constraint(self, const: Constraint) -> None:
def rename_table(self, *arg, **kw):
self.batch.append(("rename_table", arg, kw))

def create_index(self, idx: Index) -> None:
self.batch.append(("create_index", (idx,), {}))
def create_index(self, idx: Index, **kw: Any) -> None:
self.batch.append(("create_index", (idx,), kw))

def drop_index(self, idx: Index) -> None:
self.batch.append(("drop_index", (idx,), {}))
def drop_index(self, idx: Index, **kw: Any) -> None:
self.batch.append(("drop_index", (idx,), kw))

def create_table_comment(self, table):
self.batch.append(("create_table_comment", (table,), {}))
Expand Down
64 changes: 46 additions & 18 deletions alembic/operations/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,13 +876,15 @@ def __init__(
*,
schema: Optional[str] = None,
unique: bool = False,
if_not_exists: Optional[bool] = None,
**kw: Any,
) -> None:
self.index_name = index_name
self.table_name = table_name
self.columns = columns
self.schema = schema
self.unique = unique
self.if_not_exists = if_not_exists
self.kw = kw

def reverse(self) -> DropIndexOp:
Expand Down Expand Up @@ -928,6 +930,7 @@ def create_index(
*,
schema: Optional[str] = None,
unique: bool = False,
if_not_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "create index" instruction using the current
Expand Down Expand Up @@ -957,24 +960,34 @@ def create_index(
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param unique: If True, create a unique index.
:param quote:
Force quoting of this column's name on or off, corresponding
to ``True`` or ``False``. When left at its default
of ``None``, the column identifier will be quoted according to
whether the name is case sensitive (identifiers with at least one
upper case character are treated as case sensitive), or if it's a
reserved word. This flag is only needed to force quoting of a
reserved word which is not known by the SQLAlchemy dialect.
:param quote: Force quoting of this column's name on or off,
corresponding to ``True`` or ``False``. When left at its default
of ``None``, the column identifier will be quoted according to
whether the name is case sensitive (identifiers with at least one
upper case character are treated as case sensitive), or if it's a
reserved word. This flag is only needed to force quoting of a
reserved word which is not known by the SQLAlchemy dialect.
:param if_not_exists: If True, adds IF NOT EXISTS operator when
creating the new index.
.. versionadded:: 1.12.0
:param \**kw: Additional keyword arguments not mentioned above are
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
"""
op = cls(
index_name, table_name, columns, schema=schema, unique=unique, **kw
index_name,
table_name,
columns,
schema=schema,
unique=unique,
if_not_exists=if_not_exists,
**kw,
)
return operations.invoke(op)

Expand Down Expand Up @@ -1016,12 +1029,14 @@ def __init__(
table_name: Optional[str] = None,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
_reverse: Optional[CreateIndexOp] = None,
**kw: Any,
) -> None:
self.index_name = index_name
self.table_name = table_name
self.schema = schema
self.if_exists = if_exists
self._reverse = _reverse
self.kw = kw

Expand Down Expand Up @@ -1065,6 +1080,7 @@ def drop_index(
table_name: Optional[str] = None,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "drop index" instruction using the current
Expand All @@ -1081,14 +1097,26 @@ def drop_index(
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the index.
.. versionadded:: 1.12.0
:param \**kw: Additional keyword arguments not mentioned above are
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
dialect specific, and passed in the form
``<dialectname>_<argname>``.
See the documentation regarding an individual dialect at
:ref:`dialect_toplevel` for detail on documented arguments.
"""
op = cls(index_name, table_name=table_name, schema=schema, **kw)
op = cls(
index_name,
table_name=table_name,
schema=schema,
if_exists=if_exists,
**kw,
)
return operations.invoke(op)

@classmethod
Expand Down
Loading

0 comments on commit 88db76e

Please sign in to comment.