diff options
Diffstat (limited to 'lib/sqlalchemy/orm/query.py')
-rw-r--r-- | lib/sqlalchemy/orm/query.py | 134 |
1 files changed, 84 insertions, 50 deletions
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index f0b6bb031..a37d8d7e9 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -70,7 +70,6 @@ class Query(object): _criterion = None _yield_per = None _lockmode = None - _lockmode_of = None _order_by = False _group_by = False _having = None @@ -1129,49 +1128,38 @@ class Query(object): """Return a new Query object with the specified locking mode. :param mode: a string representing the desired locking mode. A - corresponding value is passed to the ``for_update`` parameter of - :meth:`~sqlalchemy.sql.expression.select` when the query is - executed. Valid values are: + corresponding :meth:`~sqlalchemy.orm.query.LockmodeArgs` object + is passed to the ``for_update`` parameter of + :meth:`~sqlalchemy.sql.expression.select` when the + query is executed. Valid values are: - ``'update'`` - passes ``for_update=True``, which translates to - ``FOR UPDATE`` (standard SQL, supported by most dialects) + ``None`` - translates to no lockmode - ``'update_nowait'`` - passes ``for_update='nowait'``, which - translates to ``FOR UPDATE NOWAIT`` (supported by Oracle, - PostgreSQL 8.1 upwards) + ``'update'`` - translates to ``FOR UPDATE`` + (standard SQL, supported by most dialects) - ``'read'`` - passes ``for_update='read'``, which translates to - ``LOCK IN SHARE MODE`` (for MySQL), and ``FOR SHARE`` (for - PostgreSQL) + ``'update_nowait'`` - translates to ``FOR UPDATE NOWAIT`` + (supported by Oracle, PostgreSQL 8.1 upwards) - ``'read_nowait'`` - passes ``for_update='read_nowait'``, which - translates to ``FOR SHARE NOWAIT`` (supported by PostgreSQL). + ``'read'`` - translates to ``LOCK IN SHARE MODE`` (for MySQL), + and ``FOR SHARE`` (for PostgreSQL) .. versionadded:: 0.7.7 ``FOR SHARE`` and ``FOR SHARE NOWAIT`` (PostgreSQL). - :param of: either a column descriptor, or list of column + + :param of: either a column descriptor, or list of column descriptors, representing the optional OF part of the - clause. This passes ``for_update_of=descriptor(s)'`` which - translates to ``FOR UPDATE OF table [NOWAIT]`` respectively + clause. This passes the descriptor to the + corresponding :meth:`~sqlalchemy.orm.query.LockmodeArgs` object, + and translates to ``FOR UPDATE OF table [NOWAIT]`` respectively ``FOR UPDATE OF table, table [NOWAIT]`` (PostgreSQL), or ``FOR UPDATE OF table.column [NOWAIT]`` respectively ``FOR UPDATE OF table.column, table.column [NOWAIT]`` (Oracle). - .. versionadded:: 0.9.0 + .. versionadded:: 0.9.0b2 """ - self._lockmode = mode - - # do not drag the ORM layer into the dialect, - # we only need the table name and column name - if isinstance(of, attributes.QueryableAttribute): - self._lockmode_of = (of.expression.table.name, - of.expression.name) - elif isinstance(of, (tuple, list)): - self._lockmode_of = [(o.expression.table.name, - o.expression.name) for o in of] - elif of is not None: - raise TypeError('OF parameter is not a column(list)') + self._lockmode = LockmodeArgs(mode=mode, of=of) @_generative() def params(self, *args, **kwargs): @@ -2704,13 +2692,6 @@ class Query(object): update_op.exec_() return update_op.rowcount - _lockmode_lookup = { - 'read': 'read', - 'read_nowait': 'read_nowait', - 'update': True, - 'update_nowait': 'nowait', - None: False - } def _compile_context(self, labels=True): context = QueryContext(self) @@ -2720,14 +2701,12 @@ class Query(object): context.labels = labels - if self._lockmode: - try: - context.for_update = self._lockmode_lookup[self._lockmode] - except KeyError: - raise sa_exc.ArgumentError( - "Unknown lockmode %r" % self._lockmode) - if self._lockmode_of is not None: - context.for_update_of = self._lockmode_of + if isinstance(self._lockmode, bool) and self._lockmode: + context.for_update = LockmodeArgs(mode='update') + elif isinstance(self._lockmode, LockmodeArgs): + if self._lockmode.mode not in LockmodeArgs.lockmodes: + raise sa_exc.ArgumentError('Unknown lockmode %r' % self._lockmode.mode) + context.for_update = self._lockmode for entity in self._entities: entity.setup_context(self, context) @@ -2813,7 +2792,6 @@ class Query(object): statement = sql.select( [inner] + context.secondary_columns, for_update=context.for_update, - for_update_of=context.for_update_of, use_labels=context.labels) from_clause = inner @@ -2859,7 +2837,6 @@ class Query(object): from_obj=context.froms, use_labels=context.labels, for_update=context.for_update, - for_update_of=context.for_update_of, order_by=context.order_by, **self._select_args ) @@ -3435,13 +3412,11 @@ class _ColumnEntity(_QueryEntity): return str(self.column) - class QueryContext(object): multi_row_eager_loaders = False adapter = None froms = () - for_update = False - for_update_of = None + for_update = None def __init__(self, query): @@ -3516,3 +3491,62 @@ class AliasOption(interfaces.MapperOption): else: alias = self.alias query._from_obj_alias = sql_util.ColumnAdapter(alias) + + +class LockmodeArgs(object): + + lockmodes = [None, + 'read', 'read_nowait', + 'update', 'update_nowait' + ] + + mode = None + of = None + + def __init__(self, mode=None, of=None): + """ORM-level Lockmode + + :class:`.LockmodeArgs` defines the locking strategy for the + dialects as given by ``FOR UPDATE [OF] [NOWAIT]``. The optional + OF component is translated by the dialects into the supported + tablename and columnname descriptors. + + :param mode: Defines the lockmode to use. + + ``None`` - translates to no lockmode + + ``'update'`` - translates to ``FOR UPDATE`` + (standard SQL, supported by most dialects) + + ``'update_nowait'`` - translates to ``FOR UPDATE NOWAIT`` + (supported by Oracle, PostgreSQL 8.1 upwards) + + ``'read'`` - translates to ``LOCK IN SHARE MODE`` (for MySQL), + and ``FOR SHARE`` (for PostgreSQL) + + ``'read_nowait'`` - translates to ``FOR SHARE NOWAIT`` + (supported by PostgreSQL). ``FOR SHARE`` and + ``FOR SHARE NOWAIT`` (PostgreSQL). + + :param of: either a column descriptor, or list of column + descriptors, representing the optional OF part of the + clause. This passes the descriptor to the + corresponding :meth:`~sqlalchemy.orm.query.LockmodeArgs` object, + and translates to ``FOR UPDATE OF table [NOWAIT]`` respectively + ``FOR UPDATE OF table, table [NOWAIT]`` (PostgreSQL), or + ``FOR UPDATE OF table.column [NOWAIT]`` respectively + ``FOR UPDATE OF table.column, table.column [NOWAIT]`` (Oracle). + + .. versionadded:: 0.9.0b2 + """ + + if isinstance(mode, bool) and mode: + mode = 'update' + + self.mode = mode + + # extract table names and column names + if isinstance(of, attributes.QueryableAttribute): + self.of = (of.expression.table.name, of.expression.name) + elif isinstance(of, (tuple, list)) and of != []: + self.of = [(o.expression.table.name, o.expression.name) for o in of] |