diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-09-13 11:39:47 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-09-16 12:52:25 -0400 |
| commit | aed2324b4de3c4f3e6cc7f801fc8ddd8e2d9012b (patch) | |
| tree | e182b5f7b2396c980a42621e1d2cf1a962dd9c67 /lib/sqlalchemy | |
| parent | d8cddf61f9de1570257c575a149738ecec6a9d91 (diff) | |
| download | sqlalchemy-aed2324b4de3c4f3e6cc7f801fc8ddd8e2d9012b.tar.gz | |
Add multivalued insert context for defaultsreview/mike_bayer/ticket_4075
Added a new method :class:`.DefaultExecutionContext.current_parameters`
which is used within a function-based default value generator in
order to retrieve the current parameters being passed to the statement.
The new function differs from the ``.current_parameters`` attribute in
that it also provides for optional grouping of parameters that
correspond to a multi-valued "insert" construct. Previously it was not
possible to identify the subset of parameters that were relevant to
the function call.
Change-Id: I6894c7b4a2bce3e83c3ade8af0e5b2f8df37b785
Fixes: #4075
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/engine/base.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/default.py | 87 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/crud.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/elements.py | 2 |
4 files changed, 90 insertions, 4 deletions
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index b5c95cb17..1719de516 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -977,7 +977,7 @@ class Connection(Connectable): except BaseException as e: self._handle_dbapi_exception(e, None, None, None, None) - ret = ctx._exec_default(default, None) + ret = ctx._exec_default(None, default, None) if self.should_close_with_result: self.close() diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 227ff0845..4b9aa9493 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -1177,10 +1177,11 @@ class DefaultExecutionContext(interfaces.ExecutionContext): self.root_connection._handle_dbapi_exception( e, None, None, None, self) - def _exec_default(self, default, type_): + def _exec_default(self, column, default, type_): if default.is_sequence: return self.fire_sequence(default, type_) elif default.is_callable: + self.current_column = column return default.arg(self) elif default.is_clause_element: # TODO: expensive branching here should be @@ -1195,17 +1196,97 @@ class DefaultExecutionContext(interfaces.ExecutionContext): else: return default.arg + current_parameters = None + """A dictionary of parameters applied to the current row. + + This attribute is only available in the context of a user-defined default + generation function, e.g. as described at :ref:`context_default_functions`. + It consists of a dictionary which includes entries for each column/value + pair that is to be part of the INSERT or UPDATE statement. The keys of the + dictionary will be the key value of each :class:`.Column`, which is usually + synonymous with the name. + + Note that the :attr:`.DefaultExecutionContext.current_parameters` attribute + does not accommodate for the "multi-values" feature of the + :meth:`.Insert.values` method. The + :meth:`.DefaultExecutionContext.get_current_parameters` method should be + preferred. + + .. seealso:: + + :meth:`.DefaultExecutionContext.get_current_parameters` + + :ref:`context_default_functions` + + """ + + def get_current_parameters(self, isolate_multiinsert_groups=True): + """Return a dictionary of parameters applied to the current row. + + This method can only be used in the context of a user-defined default + generation function, e.g. as described at + :ref:`context_default_functions`. When invoked, a dictionary is + returned which includes entries for each column/value pair that is part + of the INSERT or UPDATE statement. The keys of the dictionary will be + the key value of each :class:`.Column`, which is usually synonymous + with the name. + + :param isolate_multiinsert_groups=True: indicates that multi-valued + INSERT contructs created using :meth:`.Insert.values` should be + handled by returning only the subset of parameters that are local + to the current column default invocation. When ``False``, the + raw parameters of the statement are returned including the + naming convention used in the case of multi-valued INSERT. + + .. versionadded:: 1.2 added + :meth:`.DefaultExecutionContext.get_current_parameters` + which provides more functionality over the existing + :attr:`.DefaultExecutionContext.current_parameters` + attribute. + + .. seealso:: + + :attr:`.DefaultExecutionContext.current_parameters` + + :ref:`context_default_functions` + + """ + try: + parameters = self.current_parameters + column = self.current_column + except AttributeError: + raise exc.InvalidRequestError( + "get_current_parameters() can only be invoked in the " + "context of a Python side column default function") + if isolate_multiinsert_groups and \ + self.isinsert and \ + self.compiled.statement._has_multi_parameters: + if column._is_multiparam_column: + index = column.index + 1 + d = {column.original.key: parameters[column.key]} + else: + d = {column.key: parameters[column.key]} + index = 0 + keys = self.compiled.statement.parameters[0].keys() + d.update( + (key, parameters["%s_m%d" % (key, index)]) + for key in keys + ) + return d + else: + return parameters + def get_insert_default(self, column): if column.default is None: return None else: - return self._exec_default(column.default, column.type) + return self._exec_default(column, column.default, column.type) def get_update_default(self, column): if column.onupdate is None: return None else: - return self._exec_default(column.onupdate, column.type) + return self._exec_default(column, column.onupdate, column.type) def _process_executemany_defaults(self): key_getter = self.compiled._key_getters_for_crud_column[2] diff --git a/lib/sqlalchemy/sql/crud.py b/lib/sqlalchemy/sql/crud.py index 5739c22f9..8421b1e66 100644 --- a/lib/sqlalchemy/sql/crud.py +++ b/lib/sqlalchemy/sql/crud.py @@ -395,7 +395,10 @@ def _create_update_prefetch_bind_param(compiler, c, process=True, name=None): class _multiparam_column(elements.ColumnElement): + _is_multiparam_column = True + def __init__(self, original, index): + self.index = index self.key = "%s_m%d" % (original.key, index + 1) self.original = original self.default = original.default diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 9213d616c..3b2bcb4ff 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -3656,6 +3656,8 @@ class ColumnClause(Immutable, ColumnElement): onupdate = default = server_default = server_onupdate = None + _is_multiparam_column = False + _memoized_property = util.group_expirable_memoized_property() def __init__(self, text, type_=None, is_literal=False, _selectable=None): |
