=======
CHANGES
=======
+
+0.8.0b1
+=======
+- general
+ - SQLAlchemy 0.8 now targets Python 2.5 and
+ above. Python 2.4 is no longer supported.
+
+ - [removed] The "sqlalchemy.exceptions"
+ synonym for "sqlalchemy.exc" is removed
+ fully. [ticket:2433]
+
+- orm
+ - [removed] The legacy "mutable" system of the
+ ORM, including the MutableType class as well
+ as the mutable=True flag on PickleType
+ and postgresql.ARRAY has been removed.
+ In-place mutations are detected by the ORM
+ using the sqlalchemy.ext.mutable extension,
+ introduced in 0.7. The removal of MutableType
+ and associated constructs removes a great
+ deal of complexity from SQLAlchemy's internals.
+ The approach performed poorly as it would incur
+ a scan of the full contents of the Session
+ when in use. [ticket:2442]
+
+ - [feature] Major rewrite of relationship()
+ internals now allow join conditions which
+ include columns pointing to themselves
+ within composite foreign keys. A new
+ API for very specialized primaryjoin conditions
+ is added, allowing conditions based on
+ SQL functions, CAST, etc. to be handled
+ by placing the annotation functions
+ remote() and foreign() inline within the
+ expression when necessary. Previous recipes
+ using the semi-private _local_remote_pairs
+ approach can be upgraded to this new
+ approach. [ticket:1401]
+
+ - [feature] New standalone function with_polymorphic()
+ provides the functionality of query.with_polymorphic()
+ in a standalone form. It can be applied to any
+ entity within a query, including as the target
+ of a join in place of the "of_type()" modifier.
+ [ticket:2333]
+
+ - [feature] Added new core function "inspect()",
+ which serves as a generic gateway to
+ introspection into mappers, objects,
+ others. The Mapper and InstanceState
+ objects have been enhanced with a public
+ API that allows inspection of mapped
+ attributes, including filters for column-bound
+ or relationship-bound properties, inspection
+ of current object state, history of
+ attributes, etc. [ticket:2208]
+
+ - [bug] The "passive" flag on Session.is_modified()
+ no longer has any effect. is_modified() in
+ all cases looks only at local in-memory
+ modified flags and will not emit any
+ SQL or invoke loader callables/initializers.
+ [ticket:2320]
+
+ - [bug] The warning emitted when using
+ delete-orphan cascade with one-to-many
+ or many-to-many without single-parent=True
+ is now an error. The ORM
+ would fail to function subsequent to this
+ warning in any case. [ticket:2405]
+
+ - [feature] Query now "auto correlates" by
+ default in the same way as select() does.
+ Previously, a Query used as a subquery
+ in another would require the correlate()
+ method be called explicitly in order to
+ correlate a table on the inside to the
+ outside. As always, correlate(None)
+ disables correlation. [ticket:2179]
+
+ - [feature] Added prefix_with() method
+ to Query, calls upon select().prefix_with()
+ to allow placement of MySQL SELECT
+ directives in statements. Courtesy
+ Diana Clarke [ticket:2443]
+ also in 0.7.7.
+
+ - [bug] Fixed bug in 0.7.6 introduced by
+ [ticket:2409] whereby column_mapped_collection
+ used against columns that were mapped as
+ joins or other indirect selectables
+ would fail to function.
+ also in 0.7.7.
+
+ - [feature] Added new flag to @validates
+ include_removes. When True, collection
+ remove and attribute del events
+ will also be sent to the validation function,
+ which accepts an additional argument
+ "is_remove" when this flag is used.
+ also in 0.7.7.
+
+ - [bug] Fixed bug whereby polymorphic_on
+ column that's not otherwise mapped on the
+ class would be incorrectly included
+ in a merge() operation, raising an error.
+ [ticket:2449]
+ also in 0.7.7.
+
+ - [bug] Fixed bug in expression annotation
+ mechanics which could lead to incorrect
+ rendering of SELECT statements with aliases
+ and joins, particularly when using
+ column_property(). [ticket:2453]
+ also in 0.7.7.
+
+ - [bug] Fixed bug which would prevent
+ OrderingList from being pickleable
+ [ticket:2454]. Courtesy Jeff Dairiki
+ also in 0.7.7.
+
+- engine
+ - [feature] Added a new system
+ for registration of new dialects in-process
+ without using an entrypoint. See the
+ docs for "Registering New Dialects".
+ [ticket:2462]
+
+ - [bug] The names of the columns on the
+ .c. attribute of a select().apply_labels()
+ is now based on <tablename>_<colkey> instead
+ of <tablename>_<colname>, for those columns
+ that have a distinctly named .key.
+ [ticket:2397]
+
+
+- sql
+ - [feature] The Inspector object can now be
+ acquired using the new inspect() service,
+ part of [ticket:2208]
+
+ - [feature] The column_reflect event now
+ accepts the Inspector object as the first
+ argument, preceding "table". Code which
+ uses the 0.7 version of this very new
+ event will need modification to add the
+ "inspector" object as the first argument.
+ [ticket:2418]
+
+ - [bug] column.label(None) now produces an
+ anonymous label, instead of returning the
+ column object itself, consistent with the behavior
+ of label(column, None). [ticket:2168]
+
+ - [bug] Removed warning when Index is created
+ with no columns; while this might not be what
+ the user intended, it is a valid use case
+ as an Index could be a placeholder for just an
+ index of a certain name.
+ also in 0.7.7.
+
+ - [feature] Added new connection event
+ dbapi_error(). Is called for all DBAPI-level
+ errors passing the original DBAPI exception
+ before SQLAlchemy modifies the state
+ of the cursor.
+ also in 0.7.7.
+
+ - [bug] If conn.begin() fails when calling
+ "with engine.begin()", the newly acquired
+ Connection is closed explicitly before
+ propagating the exception onward normally.
+ also in 0.7.7.
+
++- sqlite
++ - [feature] the SQLite date and time types
++ have been overhauled to support a more open
++ ended format for input and output, using
++ name based format strings and regexps. A
++ new argument "microseconds" also provides
++ the option to omit the "microseconds"
++ portion of timestamps. Thanks to
++ Nathan Wright for the work and tests on
++ this. [ticket:2363]
++
+- mssql
+ - [bug] removed legacy behavior whereby
+ a column comparison to a scalar SELECT via
+ == would coerce to an IN with the SQL server
+ dialect. This is implicit
+ behavior which fails in other scenarios
+ so is removed. Code which relies on this
+ needs to be modified to use column.in_(select)
+ explicitly. [ticket:2277]
+
+ - [feature] Added interim create_engine flag
+ supports_unicode_binds to PyODBC dialect,
+ to force whether or not the dialect
+ passes Python unicode literals to PyODBC
+ or not.
+ also in 0.7.7.
+
+ - [bug] Repaired the use_scope_identity
+ create_engine() flag when using the pyodbc
+ dialect. Previously this flag would be
+ ignored if set to False. When set to False,
+ you'll get "SELECT @@identity" after each
+ INSERT to get at the last inserted ID,
+ for those tables which have "implicit_returning"
+ set to False.
+ also in 0.7.7.
+
+ - [bug] UPDATE..FROM syntax with SQL Server
+ requires that the updated table be present
+ in the FROM clause when an alias of that
+ table is also present in the FROM clause.
+ The updated table is now always present
+ in the FROM, when FROM is present
+ in the first place. Courtesy sayap.
+ [ticket:2468]
+ also in 0.7.7.
+
+- postgresql
+ - [feature] Added new for_update/with_lockmode()
+ options for Postgresql: for_update="read"/
+ with_lockmode("read"),
+ for_update="read_nowait"/
+ with_lockmode("read_nowait").
+ These emit "FOR SHARE" and "FOR SHARE NOWAIT",
+ respectively. Courtesy Diana Clarke
+ [ticket:2445]
+ also in 0.7.7.
+
+- mysql
+ - [bug] Fixed bug whereby column name inside
+ of "KEY" clause for autoincrement composite
+ column with InnoDB would double quote a
+ name that's a reserved word. Courtesy Jeff
+ Dairiki. [ticket:2460]
+ also in 0.7.7.
+
+ - [bug] Fixed bug whereby get_view_names() for
+ "information_schema" schema would fail
+ to retrieve views marked as "SYSTEM VIEW".
+ courtesy Matthew Turland.
+ also in 0.7.7.
+
+ - [bug] Fixed bug whereby if cast() is used
+ on a SQL expression whose type is not supported
+ by cast() and therefore CAST isn't rendered by
+ the dialect, the order of evaluation could change
+ if the casted expression required that it be
+ grouped; grouping is now applied to those
+ expressions. [ticket:2467]
+ also in 0.7.7.
+
+- extensions
+ - [removed] The SQLSoup extension is removed from
+ SQLAlchemy, and is now an external project.
+ See http://pypi.python.org/pypi/sqlsoup .
+ [ticket:2262]
+
+0.7.7 - 0.7.xx
+==============
+
+Changes which apply to 0.7.7 and subsequent versions of 0.7
+are listed in the CHANGES file within the 0.7 branch. All
+those changes which are also in the 0.8 series (which is typically
+all of them) are listed inline within the 0.8 changes above,
+those which apply to an 0.7 release are noted.
+
0.7.6
=====
- orm
from sqlalchemy.dialects.sqlite import DATETIME
dt = DATETIME(
- storage_format="%04d/%02d/%02d %02d-%02d-%02d-%06d",
- regexp=re.compile("(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)(?:-(\d+))?")
+ storage_format="%(year)04d/%(month)02d/%(day)02d %(hour)02d:%(min)02d:%(second)02d",
+ regexp=re.compile("(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)")
)
- :param storage_format: format string which will be appled to the
+ :param storage_format: format string which will be applied to the
- tuple ``(value.year, value.month, value.day, value.hour,
- value.minute, value.second, value.microsecond)``, given a
- Python datetime.datetime() object.
+ dict with keys year, month, day, hour, minute, second, and microsecond.
:param regexp: regular expression which will be applied to
- incoming result rows. The resulting match object is applied to
- the Python datetime() constructor via ``*map(int,
- match_obj.groups(0))``.
+ incoming result rows. If the regexp contains named groups, the
- resulting match dict is appled to the Python datetime() constructor
++ resulting match dict is applied to the Python datetime() constructor
+ as keyword arguments. Otherwise, if positional groups are used, the
+ the datetime() constructor is called with positional arguments via
+ ``*map(int, match_obj.groups(0))``.
"""
- _storage_format = "%04d-%02d-%02d %02d:%02d:%02d.%06d"
+ _storage_format = (
+ "%(year)04d-%(month)02d-%(day)02d "
+ "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
+ )
+
+ def __init__(self, *args, **kwargs):
+ truncate_microseconds = kwargs.pop('truncate_microseconds', False)
+ super(DATETIME, self).__init__(*args, **kwargs)
+ if truncate_microseconds:
+ assert 'storage_format' not in kwargs, "You can specify only "\
+ "one of truncate_microseconds or storage_format."
+ assert 'regexp' not in kwargs, "You can specify only one of "\
+ "truncate_microseconds or regexp."
+ self._storage_format = (
+ "%(year)04d-%(month)02d-%(day)02d "
+ "%(hour)02d:%(minute)02d:%(second)02d"
+ )
def bind_processor(self, dialect):
datetime_datetime = datetime.datetime
from sqlalchemy.dialects.sqlite import DATE
d = DATE(
- storage_format="%02d/%02d/%02d",
- regexp=re.compile("(\d+)/(\d+)/(\d+)")
+ storage_format="%(month)02d/%(day)02d/%(year)04d",
+ regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)")
)
- :param storage_format: format string which will be appled to the
+ :param storage_format: format string which will be applied to the
- tuple ``(value.year, value.month, value.day)``,
- given a Python datetime.date() object.
+ dict with keys year, month, and day.
:param regexp: regular expression which will be applied to
- incoming result rows. The resulting match object is applied to
- the Python date() constructor via ``*map(int,
- match_obj.groups(0))``.
-
+ incoming result rows. If the regexp contains named groups, the
- resulting match dict is appled to the Python date() constructor
++ resulting match dict is applied to the Python date() constructor
+ as keyword arguments. Otherwise, if positional groups are used, the
+ the date() constructor is called with positional arguments via
+ ``*map(int, match_obj.groups(0))``.
"""
- _storage_format = "%04d-%02d-%02d"
+ _storage_format = "%(year)04d-%(month)02d-%(day)02d"
def bind_processor(self, dialect):
datetime_date = datetime.date
regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?")
)
- :param storage_format: format string which will be applied
- to the tuple ``(value.hour, value.minute, value.second,
- value.microsecond)``, given a Python datetime.time() object.
- :param storage_format: format string which will be appled to the
++ :param storage_format: format string which will be applied to the
+ dict with keys hour, minute, second, and microsecond.
:param regexp: regular expression which will be applied to
- incoming result rows. The resulting match object is applied to
- the Python time() constructor via ``*map(int,
- match_obj.groups(0))``.
-
+ incoming result rows. If the regexp contains named groups, the
- resulting match dict is appled to the Python time() constructor
++ resulting match dict is applied to the Python time() constructor
+ as keyword arguments. Otherwise, if positional groups are used, the
+ the time() constructor is called with positional arguments via
+ ``*map(int, match_obj.groups(0))``.
"""
- _storage_format = "%02d:%02d:%02d.%06d"
+ _storage_format = "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
+
+ def __init__(self, *args, **kwargs):
+ truncate_microseconds = kwargs.pop('truncate_microseconds', False)
+ super(TIME, self).__init__(*args, **kwargs)
+ if truncate_microseconds:
+ assert 'storage_format' not in kwargs, "You can specify only "\
+ "one of truncate_microseconds or storage_format."
+ assert 'regexp' not in kwargs, "You can specify only one of "\
+ "truncate_microseconds or regexp."
+ self._storage_format = "%(hour)02d:%(minute)02d:%(second)02d"
def bind_processor(self, dialect):
datetime_time = datetime.time