diff options
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/persistence.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/crud.py | 26 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/dml.py | 15 |
4 files changed, 36 insertions, 12 deletions
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py index d89a93dd3..ea1c08f67 100644 --- a/lib/sqlalchemy/orm/persistence.py +++ b/lib/sqlalchemy/orm/persistence.py @@ -1220,6 +1220,8 @@ class BulkUpdate(BulkUD): def __init__(self, query, values, update_kwargs): super(BulkUpdate, self).__init__(query) self.values = values + # Accept values as a dictionary or any other iterable of value pairs + self.values = util.OrderedDict(values) self.update_kwargs = update_kwargs @classmethod @@ -1258,7 +1260,7 @@ class BulkUpdate(BulkUD): "Invalid expression type: %r" % key) def _do_exec(self): - values = dict( + values = util.OrderedDict( (self._resolve_string_to_expr(k), v) for k, v in self.values.items() ) diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 691195772..768d4f83a 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -1971,7 +1971,8 @@ class SQLCompiler(Compiled): table_text = self.update_tables_clause(update_stmt, update_stmt.table, extra_froms, **kw) - crud_params = crud._get_crud_params(self, update_stmt, **kw) + crud_params = crud._get_crud_params(self, update_stmt, keep_order=True, + **kw) if update_stmt._hints: dialect_hints, table_text = self._setup_crud_hints( diff --git a/lib/sqlalchemy/sql/crud.py b/lib/sqlalchemy/sql/crud.py index e6f16b698..614f9413b 100644 --- a/lib/sqlalchemy/sql/crud.py +++ b/lib/sqlalchemy/sql/crud.py @@ -26,7 +26,7 @@ values present. """) -def _get_crud_params(compiler, stmt, **kw): +def _get_crud_params(compiler, stmt, keep_order=False, **kw): """create a set of tuples representing column/string pairs for use in an INSERT or UPDATE statement. @@ -64,12 +64,12 @@ def _get_crud_params(compiler, stmt, **kw): # if we have statement parameters - set defaults in the # compiled params if compiler.column_keys is None: - parameters = {} + parameters = util.OrderedDict() else: - parameters = dict((_column_as_key(key), REQUIRED) - for key in compiler.column_keys - if not stmt_parameters or - key not in stmt_parameters) + parameters = util.OrderedDict((_column_as_key(key), REQUIRED) + for key in compiler.column_keys + if not stmt_parameters or + key not in stmt_parameters) # create a list of column assignment clauses as tuples values = [] @@ -97,7 +97,7 @@ def _get_crud_params(compiler, stmt, **kw): _scan_cols( compiler, stmt, parameters, _getattr_col_key, _column_as_key, - _col_bind_name, check_columns, values, kw) + _col_bind_name, check_columns, values, kw, keep_order=keep_order) if parameters and stmt_parameters: check = set(parameters).intersection( @@ -202,7 +202,7 @@ def _scan_insert_from_select_cols( def _scan_cols( compiler, stmt, parameters, _getattr_col_key, - _column_as_key, _col_bind_name, check_columns, values, kw): + _column_as_key, _col_bind_name, check_columns, values, kw, keep_order): need_pks, implicit_returning, \ implicit_return_defaults, postfetch_lastrowid = \ @@ -210,6 +210,16 @@ def _scan_cols( cols = stmt.table.columns + if keep_order: + # Order columns with parameters first, preserving their original order, + # and then the rest of the columns + keys = tuple(parameters.keys()) if parameters else tuple() + table_cols = tuple(cols) + cols = sorted(table_cols, + key=(lambda x: keys.index(_getattr_col_key(x)) + if _getattr_col_key(x) in keys + else len(keys) + table_cols.index(x))) + for c in cols: col_key = _getattr_col_key(c) if col_key in parameters and col_key not in check_columns: diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py index 6756f1554..983fed2b5 100644 --- a/lib/sqlalchemy/sql/dml.py +++ b/lib/sqlalchemy/sql/dml.py @@ -15,6 +15,7 @@ from .elements import ClauseElement, _literal_as_text, Null, and_, _clone, \ from .selectable import _interpret_as_from, _interpret_as_select, HasPrefixes from .. import util from .. import exc +from sqlalchemy.sql import schema class UpdateBase(DialectKWArgs, HasPrefixes, Executable, ClauseElement): @@ -30,16 +31,26 @@ class UpdateBase(DialectKWArgs, HasPrefixes, Executable, ClauseElement): _prefixes = () def _process_colparams(self, parameters): + def is_value_pair_dict(params): + # Check if params is a value list/tuple representing a dictionary + return ( + isinstance(params, (list, tuple)) and + all(isinstance(p, (list, tuple)) and len(p) == 2 and + isinstance(p[0], schema.Column) for p in params)) + def process_single(p): if isinstance(p, (list, tuple)): - return dict( + if is_value_pair_dict(p): + return util.OrderedDict(p) + return util.OrderedDict( (c.key, pval) for c, pval in zip(self.table.c, p) ) else: return p - if (isinstance(parameters, (list, tuple)) and parameters and + if (not is_value_pair_dict(parameters) and + isinstance(parameters, (list, tuple)) and parameters and isinstance(parameters[0], (list, tuple, dict))): if not self._supports_multi_parameters: |
