From: Mike Bayer Date: Tue, 24 Apr 2012 19:49:52 +0000 (-0400) Subject: - [feature] the SQLite date and time types X-Git-Tag: rel_0_8_0b1~457 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=59ce77ca93bc3a0af054fbead17e927172047165;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - [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] --- 59ce77ca93bc3a0af054fbead17e927172047165 diff --cc CHANGES index 409c2df213,0c4267fb0a..249b2db91f --- a/CHANGES +++ b/CHANGES @@@ -3,266 -3,6 +3,277 @@@ ======= 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 _ instead + of _, 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 diff --cc lib/sqlalchemy/dialects/sqlite/base.py index b9cd783bef,8c022342fa..9582db8f8a --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@@ -99,22 -96,38 +96,38 @@@ class DATETIME(_DateTimeMixin, sqltypes 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 @@@ -160,22 -186,22 +186,22 @@@ class DATE(_DateTimeMixin, sqltypes.Dat 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\d+)/(?P\d+)/(?P\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 @@@ -221,18 -249,28 +249,28 @@@ class TIME(_DateTimeMixin, sqltypes.Tim 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