Skip to content

Commit cc718cc

Browse files
committed
Run row value processors up front
as part of a larger series of changes to generalize row-tuples, RowProxy becomes plain Row and is no longer a "proxy"; the DBAPI row is now copied directly into the Row when constructed, result handling occurs at once. Subsequent changes will break out Row into a new version that behaves fully a tuple. Change-Id: I2ffa156afce5d21c38f28e54c3a531f361345dd5
1 parent a3c9642 commit cc718cc

18 files changed

Lines changed: 1455 additions & 948 deletions

File tree

doc/build/changelog/migration_14.rst

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,63 @@ as::
535535

536536
:ticket:`4753`
537537

538+
.. _change_4710_row:
539+
540+
The "RowProxy" is no longer a "proxy", now called ``Row``
541+
---------------------------------------------------------
542+
543+
Since the beginning of SQLAlchemy, the Core result objects exposed to the
544+
user are the :class:`.ResultProxy` and ``RowProxy`` objects. The name
545+
"proxy" refers to the `GOF Proxy Pattern <https://en.wikipedia.org/wiki/Proxy_pattern>`_,
546+
emphasizing that these objects are presenting a facade around the DBAPI
547+
``cursor`` object and the tuple-like objects returned by methods such
548+
as ``cursor.fetchone()``; as methods on the result and row proxy objects
549+
are invoked, the underlying methods or data members of the ``cursor`` and
550+
the tuple-like objects returned are invoked.
551+
552+
In particular, SQLAlchemy's row-processing functions would be invoked
553+
as a particular column in a row is accessed. By row-processing functions,
554+
we refer to functions such as that of the :class:`.Unicode` datatype, which under
555+
Python 2 would often convert Python string objects to Python unicode
556+
objects, as well as numeric functions that produce ``Decimal`` objects,
557+
SQLite datetime functions that produce ``datetime`` objects from string
558+
representations, as well as any-number of user-defined functions which can
559+
be created using :class:`.TypeDecorator`.
560+
561+
The rationale for this pattern was performance, where the anticipated use
562+
case of fetching a row from a legacy database that contained dozens of
563+
columns would not need to run, for example, a unicode converter on every
564+
element of each row, if only a few columns in the row were being fetched.
565+
SQLAlchemy eventually gained C extensions which allowed for additional
566+
performance gains within this process.
567+
568+
As part of SQLAlchemy 1.4's goal of migrating towards SQLAlchemy 2.0's updated
569+
usage patterns, row objects will be made to behave more like tuples. To
570+
suit this, the "proxy" behavior of :class:`.Row` has been removed and instead
571+
the row is populated with its final data values upon construction. This
572+
in particular allows an operation such as ``obj in row`` to work as that
573+
of a tuple where it tests for containment of ``obj`` in the row itself,
574+
rather than considering it to be a key in a mapping as is the case now.
575+
For the moment, ``obj in row`` still does a key lookup,
576+
that is, detects if the row has a particular column name as ``obj``, however
577+
this behavior is deprecated and in 2.0 the :class:`.Row` will behave fully
578+
as a tuple-like object; lookup of keys will be via the ``._mapping``
579+
attribute.
580+
581+
The result of removing the proxy behavior from rows is that the C code has been
582+
simplified and the performance of many operations is improved both with and
583+
without the C extensions in use. Modern Python DBAPIs handle unicode
584+
conversion natively in most cases, and SQLAlchemy's unicode handlers are
585+
very fast in any case, so the expense of unicode conversion
586+
is a non-issue.
587+
588+
This change by itself has no behavioral impact on the row, but is part of
589+
a larger series of changes in :ticket:`4710` which unifies the Core row/result
590+
facade with that of the ORM.
591+
592+
:ticket:`4710`
593+
594+
538595
.. _change_4449:
539596

540597
Improved column labeling for simple column expressions using CAST or similar
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.. change::
2+
:tags: feature, engine
3+
4+
The ``RowProxy`` class is no longer a "proxy" object, and is instead
5+
directly populated with the post-processed contents of the DBAPI row tuple
6+
upon construction. Now named :class:`.Row`, the mechanics of how the
7+
Python-level value processors have been simplified, particularly as it impacts the
8+
format of the C code, so that a DBAPI row is processed into a result tuple
9+
up front. See the migration notes for further details.
10+
11+
.. seealso::
12+
13+
:ref:`change_4710_row`

doc/build/core/connections.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ Connection / Engine API
657657
:members:
658658
:private-members: _soft_close
659659

660-
.. autoclass:: RowProxy
660+
.. autoclass:: Row
661661
:members:
662662

663663
.. autoclass:: Transaction

0 commit comments

Comments
 (0)