summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/persistence.py4
-rw-r--r--lib/sqlalchemy/sql/compiler.py3
-rw-r--r--lib/sqlalchemy/sql/crud.py26
-rw-r--r--lib/sqlalchemy/sql/dml.py15
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: