]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [feature] the SQLite date and time types
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 24 Apr 2012 19:49:52 +0000 (15:49 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 24 Apr 2012 19:49:52 +0000 (15:49 -0400)
    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]

1  2 
CHANGES
lib/sqlalchemy/dialects/sqlite/base.py

diff --cc CHANGES
index 409c2df2136aacf56dfadde465fe6f80230e59c0,0c4267fb0a7690a472e471141d0d3f5063635ea6..249b2db91f12f26a48b2646912cadfebebcc8c68
+++ b/CHANGES
  =======
  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
index b9cd783beff9eded37ff618097d3f06df6644a26,8c022342fa405abba1396fb01822f142a00c7bd6..9582db8f8ad82d6af9c9b1251f3f0980abd607d9
@@@ -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<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
@@@ -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