summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/engine/default.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2019-06-04 17:29:20 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-02-21 17:53:33 -0500
commitf559f378c47811b5528ad1769cb86925e85fd1e5 (patch)
treefd8325501a96cf1e4280c15f267f63b2af7b5f97 /lib/sqlalchemy/engine/default.py
parent93b7767d00267ebe149cabcae7246b6796352eb8 (diff)
downloadsqlalchemy-f559f378c47811b5528ad1769cb86925e85fd1e5.tar.gz
Result initial introduction
This builds on cc718cccc0bf8a01abdf4068c7ea4f3 which moved RowProxy to Row, allowing Row to be more like a named tuple. - KeyedTuple in ORM is replaced with Row - ResultSetMetaData broken out into "simple" and "cursor" versions for ORM and Core, as well as LegacyCursor version. - Row now has _mapping attribute that supplies full mapping behavior. Row and SimpleRow both have named tuple behavior otherwise. LegacyRow has some mapping features on the tuple which emit deprecation warnings (e.g. keys(), values(), etc). the biggest change for mapping->tuple is the behavior of __contains__ which moves from testing of "key in row" to "value in row". - ResultProxy breaks into ResultProxy and FutureResult (interim), the latter has the newer APIs. Made available to dialects using execution options. - internal reflection methods and most tests move off of implicit Row mapping behavior and move to row._mapping, result.mappings() method using future result - a new strategy system for cursor handling replaces the various subclasses of RowProxy - some execution context adjustments. We will leave EC in but refined things like get_result_proxy() and out parameter handling. Dialects for 1.4 will need to adjust from get_result_proxy() to get_result_cursor_strategy(), if they are using this method - out parameter handling now accommodated by get_out_parameter_values() EC method. Oracle changes for this. external dialect for DB2 for example will also need to adjust for this. - deprecate case_insensitive flag for engine / result, this feature is not used mapping-methods on Row are deprecated, and replaced with Row._mapping.<meth>, including: row.keys() -> use row._mapping.keys() row.items() -> use row._mapping.items() row.values() -> use row._mapping.values() key in row -> use key in row._mapping int in row -> use int < len(row) Fixes: #4710 Fixes: #4878 Change-Id: Ieb9085e9bcff564359095b754da9ae0af55679f0
Diffstat (limited to 'lib/sqlalchemy/engine/default.py')
-rw-r--r--lib/sqlalchemy/engine/default.py114
1 files changed, 88 insertions, 26 deletions
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 7d36345fd..7efc4bda2 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -19,7 +19,7 @@ import re
import weakref
from . import interfaces
-from . import result
+from . import result as _result
from .. import event
from .. import exc
from .. import pool
@@ -201,6 +201,13 @@ class DefaultDialect(interfaces.Dialect):
'expressions, or an "empty set" SELECT, at statement execution'
"time.",
),
+ case_sensitive=(
+ "1.4",
+ "The :paramref:`.create_engine.case_sensitive` parameter "
+ "is deprecated and will be removed in a future release. "
+ "Applications should work with result column names in a case "
+ "sensitive fashion.",
+ ),
)
def __init__(
self,
@@ -667,6 +674,8 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
returned_defaults = None
_is_implicit_returning = False
_is_explicit_returning = False
+ _is_future_result = False
+ _is_server_side = False
# a hook for SQLite's translation of
# result column names
@@ -725,6 +734,9 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
# we get here
assert compiled.can_execute
+ self._is_future_result = connection._execution_options.get(
+ "future_result", False
+ )
self.execution_options = compiled.execution_options.union(
connection._execution_options
)
@@ -860,6 +872,10 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
self.dialect = connection.dialect
self.is_text = True
+ self._is_future_result = connection._execution_options.get(
+ "future_result", False
+ )
+
# plain text statement
self.execution_options = connection._execution_options
@@ -1035,6 +1051,11 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
def pre_exec(self):
pass
+ def get_out_parameter_values(self, names):
+ raise NotImplementedError(
+ "This dialect does not support OUT parameters"
+ )
+
def post_exec(self):
pass
@@ -1051,27 +1072,18 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
def get_lastrowid(self):
"""return self.cursor.lastrowid, or equivalent, after an INSERT.
- This may involve calling special cursor functions,
- issuing a new SELECT on the cursor (or a new one),
- or returning a stored value that was
+ This may involve calling special cursor functions, issuing a new SELECT
+ on the cursor (or a new one), or returning a stored value that was
calculated within post_exec().
- This function will only be called for dialects
- which support "implicit" primary key generation,
- keep preexecute_autoincrement_sequences set to False,
- and when no explicit id value was bound to the
- statement.
+ This function will only be called for dialects which support "implicit"
+ primary key generation, keep preexecute_autoincrement_sequences set to
+ False, and when no explicit id value was bound to the statement.
- The function is called once, directly after
- post_exec() and before the transaction is committed
- or ResultProxy is generated. If the post_exec()
- method assigns a value to `self._lastrowid`, the
- value is used in place of calling get_lastrowid().
-
- Note that this method is *not* equivalent to the
- ``lastrowid`` method on ``ResultProxy``, which is a
- direct proxy to the DBAPI ``lastrowid`` accessor
- in all cases.
+ The function is called once for an INSERT statement that would need to
+ return the last inserted primary key for those dialects that make use
+ of the lastrowid concept. In these cases, it is called directly after
+ :meth:`.ExecutionContext.post_exec`.
"""
return self.cursor.lastrowid
@@ -1079,11 +1091,13 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
def handle_dbapi_exception(self, e):
pass
- def get_result_proxy(self):
+ def get_result_cursor_strategy(self, result):
if self._is_server_side:
- return result.BufferedRowResultProxy(self)
+ strat_cls = _result.BufferedRowCursorFetchStrategy
else:
- return result.ResultProxy(self)
+ strat_cls = _result.DefaultCursorFetchStrategy
+
+ return strat_cls.create(result)
@property
def rowcount(self):
@@ -1095,6 +1109,49 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
def supports_sane_multi_rowcount(self):
return self.dialect.supports_sane_multi_rowcount
+ def _setup_result_proxy(self):
+ if self.is_crud or self.is_text:
+ result = self._setup_crud_result_proxy()
+ else:
+ result = _result.ResultProxy._create_for_context(self)
+
+ if (
+ self.compiled
+ and not self.isddl
+ and self.compiled.has_out_parameters
+ ):
+ self._setup_out_parameters(result)
+
+ return result
+
+ def _setup_out_parameters(self, result):
+
+ out_bindparams = [
+ (param, name)
+ for param, name in self.compiled.bind_names.items()
+ if param.isoutparam
+ ]
+ out_parameters = {}
+
+ for bindparam, raw_value in zip(
+ [param for param, name in out_bindparams],
+ self.get_out_parameter_values(
+ [name for param, name in out_bindparams]
+ ),
+ ):
+
+ type_ = bindparam.type
+ impl_type = type_.dialect_impl(self.dialect)
+ dbapi_type = impl_type.get_dbapi_type(self.dialect.dbapi)
+ result_processor = impl_type.result_processor(
+ self.dialect, dbapi_type
+ )
+ if result_processor is not None:
+ raw_value = result_processor(raw_value)
+ out_parameters[bindparam.key] = raw_value
+
+ result.out_parameters = out_parameters
+
def _setup_crud_result_proxy(self):
if self.isinsert and not self.executemany:
if (
@@ -1108,11 +1165,11 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
elif not self._is_implicit_returning:
self._setup_ins_pk_from_empty()
- result = self.get_result_proxy()
+ result = _result.ResultProxy._create_for_context(self)
if self.isinsert:
if self._is_implicit_returning:
- row = result.fetchone()
+ row = result._onerow()
self.returned_defaults = row
self._setup_ins_pk_from_implicit_returning(row)
result._soft_close()
@@ -1121,7 +1178,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
result._soft_close()
result._metadata = None
elif self.isupdate and self._is_implicit_returning:
- row = result.fetchone()
+ row = result._onerow()
self.returned_defaults = row
result._soft_close()
result._metadata = None
@@ -1179,8 +1236,13 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
key_getter = self.compiled._key_getters_for_crud_column[2]
table = self.compiled.statement.table
compiled_params = self.compiled_parameters[0]
+
+ # TODO: why are we using keyed index here? can't we get the ints?
+ # can compiler build up the structure here as far as what was
+ # explicit and what comes back in returning?
+ row_mapping = row._mapping
self.inserted_primary_key = [
- row[col] if value is None else value
+ row_mapping[col] if value is None else value
for col, value in [
(col, compiled_params.get(key_getter(col), None))
for col in table.primary_key