Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/alembic/operations/base.py : 71%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from contextlib import contextmanager
2import textwrap
4from . import batch
5from . import schemaobj
6from .. import util
7from ..util import sqla_compat
8from ..util.compat import exec_
9from ..util.compat import inspect_formatargspec
10from ..util.compat import inspect_getargspec
12__all__ = ("Operations", "BatchOperations")
14try:
15 from sqlalchemy.sql.naming import conv
16except:
17 conv = None
20class Operations(util.ModuleClsProxy):
22 """Define high level migration operations.
24 Each operation corresponds to some schema migration operation,
25 executed against a particular :class:`.MigrationContext`
26 which in turn represents connectivity to a database,
27 or a file output stream.
29 While :class:`.Operations` is normally configured as
30 part of the :meth:`.EnvironmentContext.run_migrations`
31 method called from an ``env.py`` script, a standalone
32 :class:`.Operations` instance can be
33 made for use cases external to regular Alembic
34 migrations by passing in a :class:`.MigrationContext`::
36 from alembic.migration import MigrationContext
37 from alembic.operations import Operations
39 conn = myengine.connect()
40 ctx = MigrationContext.configure(conn)
41 op = Operations(ctx)
43 op.alter_column("t", "c", nullable=True)
45 Note that as of 0.8, most of the methods on this class are produced
46 dynamically using the :meth:`.Operations.register_operation`
47 method.
49 """
51 _to_impl = util.Dispatcher()
53 def __init__(self, migration_context, impl=None):
54 """Construct a new :class:`.Operations`
56 :param migration_context: a :class:`.MigrationContext`
57 instance.
59 """
60 self.migration_context = migration_context
61 if impl is None:
62 self.impl = migration_context.impl
63 else:
64 self.impl = impl
66 self.schema_obj = schemaobj.SchemaObjects(migration_context)
68 @classmethod
69 def register_operation(cls, name, sourcename=None):
70 """Register a new operation for this class.
72 This method is normally used to add new operations
73 to the :class:`.Operations` class, and possibly the
74 :class:`.BatchOperations` class as well. All Alembic migration
75 operations are implemented via this system, however the system
76 is also available as a public API to facilitate adding custom
77 operations.
79 .. versionadded:: 0.8.0
81 .. seealso::
83 :ref:`operation_plugins`
86 """
88 def register(op_cls):
89 if sourcename is None:
90 fn = getattr(op_cls, name)
91 source_name = fn.__name__
92 else:
93 fn = getattr(op_cls, sourcename)
94 source_name = fn.__name__
96 spec = inspect_getargspec(fn)
98 name_args = spec[0]
99 assert name_args[0:2] == ["cls", "operations"]
101 name_args[0:2] = ["self"]
103 args = inspect_formatargspec(*spec)
104 num_defaults = len(spec[3]) if spec[3] else 0
105 if num_defaults:
106 defaulted_vals = name_args[0 - num_defaults :]
107 else:
108 defaulted_vals = ()
110 apply_kw = inspect_formatargspec(
111 name_args,
112 spec[1],
113 spec[2],
114 defaulted_vals,
115 formatvalue=lambda x: "=" + x,
116 )
118 func_text = textwrap.dedent(
119 """\
120 def %(name)s%(args)s:
121 %(doc)r
122 return op_cls.%(source_name)s%(apply_kw)s
123 """
124 % {
125 "name": name,
126 "source_name": source_name,
127 "args": args,
128 "apply_kw": apply_kw,
129 "doc": fn.__doc__,
130 "meth": fn.__name__,
131 }
132 )
133 globals_ = {"op_cls": op_cls}
134 lcl = {}
135 exec_(func_text, globals_, lcl)
136 setattr(cls, name, lcl[name])
137 fn.__func__.__doc__ = (
138 "This method is proxied on "
139 "the :class:`.%s` class, via the :meth:`.%s.%s` method."
140 % (cls.__name__, cls.__name__, name)
141 )
142 if hasattr(fn, "_legacy_translations"):
143 lcl[name]._legacy_translations = fn._legacy_translations
144 return op_cls
146 return register
148 @classmethod
149 def implementation_for(cls, op_cls):
150 """Register an implementation for a given :class:`.MigrateOperation`.
152 This is part of the operation extensibility API.
154 .. seealso::
156 :ref:`operation_plugins` - example of use
158 """
160 def decorate(fn):
161 cls._to_impl.dispatch_for(op_cls)(fn)
162 return fn
164 return decorate
166 @classmethod
167 @contextmanager
168 def context(cls, migration_context):
169 op = Operations(migration_context)
170 op._install_proxy()
171 yield op
172 op._remove_proxy()
174 @contextmanager
175 def batch_alter_table(
176 self,
177 table_name,
178 schema=None,
179 recreate="auto",
180 partial_reordering=None,
181 copy_from=None,
182 table_args=(),
183 table_kwargs=util.immutabledict(),
184 reflect_args=(),
185 reflect_kwargs=util.immutabledict(),
186 naming_convention=None,
187 ):
188 """Invoke a series of per-table migrations in batch.
190 Batch mode allows a series of operations specific to a table
191 to be syntactically grouped together, and allows for alternate
192 modes of table migration, in particular the "recreate" style of
193 migration required by SQLite.
195 "recreate" style is as follows:
197 1. A new table is created with the new specification, based on the
198 migration directives within the batch, using a temporary name.
200 2. the data copied from the existing table to the new table.
202 3. the existing table is dropped.
204 4. the new table is renamed to the existing table name.
206 The directive by default will only use "recreate" style on the
207 SQLite backend, and only if directives are present which require
208 this form, e.g. anything other than ``add_column()``. The batch
209 operation on other backends will proceed using standard ALTER TABLE
210 operations.
212 The method is used as a context manager, which returns an instance
213 of :class:`.BatchOperations`; this object is the same as
214 :class:`.Operations` except that table names and schema names
215 are omitted. E.g.::
217 with op.batch_alter_table("some_table") as batch_op:
218 batch_op.add_column(Column('foo', Integer))
219 batch_op.drop_column('bar')
221 The operations within the context manager are invoked at once
222 when the context is ended. When run against SQLite, if the
223 migrations include operations not supported by SQLite's ALTER TABLE,
224 the entire table will be copied to a new one with the new
225 specification, moving all data across as well.
227 The copy operation by default uses reflection to retrieve the current
228 structure of the table, and therefore :meth:`.batch_alter_table`
229 in this mode requires that the migration is run in "online" mode.
230 The ``copy_from`` parameter may be passed which refers to an existing
231 :class:`.Table` object, which will bypass this reflection step.
233 .. note:: The table copy operation will currently not copy
234 CHECK constraints, and may not copy UNIQUE constraints that are
235 unnamed, as is possible on SQLite. See the section
236 :ref:`sqlite_batch_constraints` for workarounds.
238 :param table_name: name of table
239 :param schema: optional schema name.
240 :param recreate: under what circumstances the table should be
241 recreated. At its default of ``"auto"``, the SQLite dialect will
242 recreate the table if any operations other than ``add_column()``,
243 ``create_index()``, or ``drop_index()`` are
244 present. Other options include ``"always"`` and ``"never"``.
245 :param copy_from: optional :class:`~sqlalchemy.schema.Table` object
246 that will act as the structure of the table being copied. If omitted,
247 table reflection is used to retrieve the structure of the table.
249 .. versionadded:: 0.7.6 Fully implemented the
250 :paramref:`~.Operations.batch_alter_table.copy_from`
251 parameter.
253 .. seealso::
255 :ref:`batch_offline_mode`
257 :paramref:`~.Operations.batch_alter_table.reflect_args`
259 :paramref:`~.Operations.batch_alter_table.reflect_kwargs`
261 :param reflect_args: a sequence of additional positional arguments that
262 will be applied to the table structure being reflected / copied;
263 this may be used to pass column and constraint overrides to the
264 table that will be reflected, in lieu of passing the whole
265 :class:`~sqlalchemy.schema.Table` using
266 :paramref:`~.Operations.batch_alter_table.copy_from`.
268 .. versionadded:: 0.7.1
270 :param reflect_kwargs: a dictionary of additional keyword arguments
271 that will be applied to the table structure being copied; this may be
272 used to pass additional table and reflection options to the table that
273 will be reflected, in lieu of passing the whole
274 :class:`~sqlalchemy.schema.Table` using
275 :paramref:`~.Operations.batch_alter_table.copy_from`.
277 .. versionadded:: 0.7.1
279 :param table_args: a sequence of additional positional arguments that
280 will be applied to the new :class:`~sqlalchemy.schema.Table` when
281 created, in addition to those copied from the source table.
282 This may be used to provide additional constraints such as CHECK
283 constraints that may not be reflected.
284 :param table_kwargs: a dictionary of additional keyword arguments
285 that will be applied to the new :class:`~sqlalchemy.schema.Table`
286 when created, in addition to those copied from the source table.
287 This may be used to provide for additional table options that may
288 not be reflected.
290 .. versionadded:: 0.7.0
292 :param naming_convention: a naming convention dictionary of the form
293 described at :ref:`autogen_naming_conventions` which will be applied
294 to the :class:`~sqlalchemy.schema.MetaData` during the reflection
295 process. This is typically required if one wants to drop SQLite
296 constraints, as these constraints will not have names when
297 reflected on this backend. Requires SQLAlchemy **0.9.4** or greater.
299 .. seealso::
301 :ref:`dropping_sqlite_foreign_keys`
303 .. versionadded:: 0.7.1
305 :param partial_reordering: a list of tuples, each suggesting a desired
306 ordering of two or more columns in the newly created table. Requires
307 that :paramref:`.batch_alter_table.recreate` is set to ``"always"``.
308 Examples, given a table with columns "a", "b", "c", and "d":
310 Specify the order of all columns::
312 with op.batch_alter_table(
313 "some_table", recreate="always",
314 partial_reordering=[("c", "d", "a", "b")]
315 ) as batch_op:
316 pass
318 Ensure "d" appears before "c", and "b", appears before "a"::
320 with op.batch_alter_table(
321 "some_table", recreate="always",
322 partial_reordering=[("d", "c"), ("b", "a")]
323 ) as batch_op:
324 pass
326 The ordering of columns not included in the partial_reordering
327 set is undefined. Therefore it is best to specify the complete
328 ordering of all columns for best results.
330 .. versionadded:: 1.4.0
332 .. note:: batch mode requires SQLAlchemy 0.8 or above.
334 .. seealso::
336 :ref:`batch_migrations`
338 """
339 impl = batch.BatchOperationsImpl(
340 self,
341 table_name,
342 schema,
343 recreate,
344 copy_from,
345 table_args,
346 table_kwargs,
347 reflect_args,
348 reflect_kwargs,
349 naming_convention,
350 partial_reordering,
351 )
352 batch_op = BatchOperations(self.migration_context, impl=impl)
353 yield batch_op
354 impl.flush()
356 def get_context(self):
357 """Return the :class:`.MigrationContext` object that's
358 currently in use.
360 """
362 return self.migration_context
364 def invoke(self, operation):
365 """Given a :class:`.MigrateOperation`, invoke it in terms of
366 this :class:`.Operations` instance.
368 .. versionadded:: 0.8.0
370 """
371 fn = self._to_impl.dispatch(
372 operation, self.migration_context.impl.__dialect__
373 )
374 return fn(self, operation)
376 def f(self, name):
377 """Indicate a string name that has already had a naming convention
378 applied to it.
380 This feature combines with the SQLAlchemy ``naming_convention`` feature
381 to disambiguate constraint names that have already had naming
382 conventions applied to them, versus those that have not. This is
383 necessary in the case that the ``"%(constraint_name)s"`` token
384 is used within a naming convention, so that it can be identified
385 that this particular name should remain fixed.
387 If the :meth:`.Operations.f` is used on a constraint, the naming
388 convention will not take effect::
390 op.add_column('t', 'x', Boolean(name=op.f('ck_bool_t_x')))
392 Above, the CHECK constraint generated will have the name
393 ``ck_bool_t_x`` regardless of whether or not a naming convention is
394 in use.
396 Alternatively, if a naming convention is in use, and 'f' is not used,
397 names will be converted along conventions. If the ``target_metadata``
398 contains the naming convention
399 ``{"ck": "ck_bool_%(table_name)s_%(constraint_name)s"}``, then the
400 output of the following:
402 op.add_column('t', 'x', Boolean(name='x'))
404 will be::
406 CONSTRAINT ck_bool_t_x CHECK (x in (1, 0)))
408 The function is rendered in the output of autogenerate when
409 a particular constraint name is already converted, for SQLAlchemy
410 version **0.9.4 and greater only**. Even though ``naming_convention``
411 was introduced in 0.9.2, the string disambiguation service is new
412 as of 0.9.4.
414 .. versionadded:: 0.6.4
416 """
417 if conv:
418 return conv(name)
419 else:
420 raise NotImplementedError(
421 "op.f() feature requires SQLAlchemy 0.9.4 or greater."
422 )
424 def inline_literal(self, value, type_=None):
425 r"""Produce an 'inline literal' expression, suitable for
426 using in an INSERT, UPDATE, or DELETE statement.
428 When using Alembic in "offline" mode, CRUD operations
429 aren't compatible with SQLAlchemy's default behavior surrounding
430 literal values,
431 which is that they are converted into bound values and passed
432 separately into the ``execute()`` method of the DBAPI cursor.
433 An offline SQL
434 script needs to have these rendered inline. While it should
435 always be noted that inline literal values are an **enormous**
436 security hole in an application that handles untrusted input,
437 a schema migration is not run in this context, so
438 literals are safe to render inline, with the caveat that
439 advanced types like dates may not be supported directly
440 by SQLAlchemy.
442 See :meth:`.execute` for an example usage of
443 :meth:`.inline_literal`.
445 The environment can also be configured to attempt to render
446 "literal" values inline automatically, for those simple types
447 that are supported by the dialect; see
448 :paramref:`.EnvironmentContext.configure.literal_binds` for this
449 more recently added feature.
451 :param value: The value to render. Strings, integers, and simple
452 numerics should be supported. Other types like boolean,
453 dates, etc. may or may not be supported yet by various
454 backends.
455 :param type\_: optional - a :class:`sqlalchemy.types.TypeEngine`
456 subclass stating the type of this value. In SQLAlchemy
457 expressions, this is usually derived automatically
458 from the Python type of the value itself, as well as
459 based on the context in which the value is used.
461 .. seealso::
463 :paramref:`.EnvironmentContext.configure.literal_binds`
465 """
466 return sqla_compat._literal_bindparam(None, value, type_=type_)
468 def get_bind(self):
469 """Return the current 'bind'.
471 Under normal circumstances, this is the
472 :class:`~sqlalchemy.engine.Connection` currently being used
473 to emit SQL to the database.
475 In a SQL script context, this value is ``None``. [TODO: verify this]
477 """
478 return self.migration_context.impl.bind
481class BatchOperations(Operations):
482 """Modifies the interface :class:`.Operations` for batch mode.
484 This basically omits the ``table_name`` and ``schema`` parameters
485 from associated methods, as these are a given when running under batch
486 mode.
488 .. seealso::
490 :meth:`.Operations.batch_alter_table`
492 Note that as of 0.8, most of the methods on this class are produced
493 dynamically using the :meth:`.Operations.register_operation`
494 method.
496 """
498 def _noop(self, operation):
499 raise NotImplementedError(
500 "The %s method does not apply to a batch table alter operation."
501 % operation
502 )